Add spoilers (and broken jank starter bbcode)

This commit is contained in:
Ben Visness 2021-06-15 23:04:01 -05:00
parent 08f20f9fed
commit 987d379223
6 changed files with 218 additions and 15 deletions

Binary file not shown.

70
src/parsing/bbcode.go Normal file
View File

@ -0,0 +1,70 @@
package parsing
import (
"fmt"
"strings"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
gast "github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)
const BBCodePriority = 1 // TODO: Pick something more reasonable?
type bParser struct{}
var _ parser.InlineParser = bParser{}
func (s bParser) Trigger() []byte {
return []byte{'['}
}
func (s bParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node {
// _, segment := block.PeekLine()
// start := segment.Start
// block.Advance(3)
// n := ast.NewTextSegment(text.NewSegment(start, start+4))
// bold := ast.NewText()
// bold.Segment
// link := ast.NewAutoLink(typ, n)
// link.Protocol = protocol
// return link
lineBytes, segment := block.PeekLine()
fmt.Printf("line: %s\n", string(lineBytes))
fmt.Printf("segment: %#v\n", segment)
line := string(lineBytes)
if !strings.HasPrefix(line, "[b]") {
return nil
}
start := 0
closingIndex := strings.Index(line, "[/b]")
if closingIndex < 0 {
return nil
}
end := closingIndex + 4
n := ast.NewEmphasis(2)
n.AppendChild(n, ast.NewString([]byte("wow bold text")))
block.Advance(end - start)
return n
}
type bTag struct{}
func (e bTag) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithInlineParsers(
util.Prioritized(bParser{}, BBCodePriority),
))
// m.Renderer().AddOptions(renderer.WithNodeRenderers(
// util.Prioritized(NewStrikethroughHTMLRenderer(), 500),
// ))
}

View File

@ -91,7 +91,7 @@ func tokenizeBBCode(input string) []token {
} }
var md = goldmark.New( var md = goldmark.New(
goldmark.WithExtensions(extension.GFM), goldmark.WithExtensions(extension.GFM, SpoilerExtension{}, bTag{}),
) )
func ParsePostInput(source string) string { func ParsePostInput(source string) string {

View File

@ -26,22 +26,28 @@ Mix 'em: [u][/i]wow.[/i][/u]
}) })
} }
func TestParsePostInput(t *testing.T) { // func TestParsePostInput(t *testing.T) {
testDoc := []byte(` // testDoc := []byte(`
Hello, *world!* // Hello, *world!*
I can do **bold**, *italic*, and _underlined_ text?? // I can do **bold**, *italic*, and _underlined_ text??
# Heading 1 // # Heading 1
## Heading 2 // ## Heading 2
### Heading 3 // ### Heading 3
Links: [HMN](https://handmade.network) // Links: [HMN](https://handmade.network)
Images: ![this is a picture of sanic](https://i.kym-cdn.com/photos/images/newsfeed/000/722/711/ef1.jpg) // Images: ![this is a picture of sanic](https://i.kym-cdn.com/photos/images/newsfeed/000/722/711/ef1.jpg)
`) // `)
res := ParsePostInput(testDoc) // res := ParsePostInput(testDoc)
fmt.Println(string(res)) // fmt.Println(string(res))
// t.Fail()
// }
func TestBBCodeParsing(t *testing.T) {
res := ParsePostInput(`[b]ONE[/b] [i]TWO[/i]`)
fmt.Println(res)
t.Fail() t.Fail()
} }

123
src/parsing/spoilers.go Normal file
View File

@ -0,0 +1,123 @@
package parsing
import (
"github.com/yuin/goldmark"
gast "github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)
// ----------------------
// Parser and delimiters
// ----------------------
type spoilerParser struct{}
func NewSpoilerParser() parser.InlineParser {
return spoilerParser{}
}
func (s spoilerParser) Trigger() []byte {
return []byte{'|'}
}
func (s spoilerParser) Parse(parent gast.Node, block text.Reader, pc parser.Context) gast.Node {
before := block.PrecendingCharacter() // ScanDelimiter needs this for shady left vs. right delimiter reasons. Who delimits the delimiters?
restOfLine, segment := block.PeekLine() // Gets the rest of the line (starting at the current parser cursor index), and the segment representing the indices in the source text.
delimiter := parser.ScanDelimiter(restOfLine, before, 2, spoilerDelimiterParser{}) // Scans a consecutive run of the trigger character. Returns a delimiter node tracking which character that was, how many of it there were, etc. We do 2 here because we want ~~spoilers~~.
if delimiter == nil {
// I guess we only saw one ~ :)
return nil
}
delimiter.Segment = segment.WithStop(segment.Start + delimiter.OriginalLength) // The delimiter needs to know exactly what source indices it corresponds to.
block.Advance(delimiter.OriginalLength) // Advance the parser past the delimiter.
pc.PushDelimiter(delimiter) // Push the delimiter onto the stack (either opening or closing; both are handled the same way as far as this method is concerned).
return delimiter
}
type spoilerDelimiterParser struct{}
func (p spoilerDelimiterParser) IsDelimiter(b byte) bool {
return b == '|'
}
func (p spoilerDelimiterParser) CanOpenCloser(opener, closer *parser.Delimiter) bool {
return opener.Char == closer.Char
}
func (p spoilerDelimiterParser) OnMatch(consumes int) gast.Node {
return NewSpoiler()
}
// ----------------------
// AST node
// ----------------------
type SpoilerNode struct {
gast.BaseInline
}
var _ gast.Node = &SpoilerNode{}
func (n *SpoilerNode) Dump(source []byte, level int) {
gast.DumpHelper(n, source, level, nil, nil)
}
var KindSpoiler = gast.NewNodeKind("Spoiler")
func (n *SpoilerNode) Kind() gast.NodeKind {
return KindSpoiler
}
func NewSpoiler() *SpoilerNode {
return &SpoilerNode{}
}
// ----------------------
// Renderer
// ----------------------
type SpoilerHTMLRenderer struct {
html.Config
}
func NewSpoilerHTMLRenderer(opts ...html.Option) renderer.NodeRenderer {
r := &SpoilerHTMLRenderer{
Config: html.NewConfig(),
}
for _, opt := range opts {
opt.SetHTMLOption(&r.Config)
}
return r
}
func (r *SpoilerHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(KindSpoiler, r.renderSpoiler)
}
func (r *SpoilerHTMLRenderer) renderSpoiler(w util.BufWriter, source []byte, n gast.Node, entering bool) (gast.WalkStatus, error) {
if entering {
_, _ = w.WriteString("<span class=\"spoiler\">")
} else {
_, _ = w.WriteString("</span>")
}
return gast.WalkContinue, nil
}
// ----------------------
// Extension
// ----------------------
type SpoilerExtension struct{}
func (e SpoilerExtension) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithInlineParsers(
util.Prioritized(NewSpoilerParser(), 500),
))
m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(NewSpoilerHTMLRenderer(), 500),
))
}

View File

@ -108,9 +108,13 @@
const tf = document.querySelector('#editor'); const tf = document.querySelector('#editor');
const preview = document.querySelector('#preview'); const preview = document.querySelector('#preview');
tf.addEventListener('input', () => { function updatePreview() {
const previewHtml = parseMarkdown(tf.value); const previewHtml = parseMarkdown(tf.value);
preview.innerHTML = previewHtml; preview.innerHTML = previewHtml;
}) }
tf.addEventListener('input', () => {
updatePreview();
});
</script> </script>
{{ end }} {{ end }}