process links and headings

This commit is contained in:
dre 2021-07-04 15:03:37 +08:00
parent 5eaedce5e6
commit 10ed27bb77
3 changed files with 44 additions and 35 deletions

View file

@ -7,18 +7,19 @@ state machines.
<!-- testing markdown, this should be deleted, below merged --> <!-- testing markdown, this should be deleted, below merged -->
See the [gemini See the [gemini
protocol](https://gemini.circumlunar.space/). protocol](https://gemini.circumlunar.space/) and the [protocol
spec](https://gemini.circumlunar.space/docs/specification.gmi).
Internally md2gmi does a 1st pass that constructs the core layout for gemtext. This is then streamed Internally md2gmi does a 1st pass that constructs the core layout for gemtext. This is then streamed
to the 2nd pass line by line. The 2nd pass will convert links and stream line by line to the output. to the 2nd pass line by line. The 2nd pass will convert links and stream line by line to the output.
### Usage ###Usage
```plain ```plain
Usage of ./md2gmi: Usage of ./md2gmi:
-in string -f string
specify a .md (Markdown) file to read from, otherwise stdin (default) specify a .md (Markdown) file to read from, otherwise stdin (default)
-out string -o string
specify a .gmi (gemtext) file to write to, otherwise stdout (default) specify a .gmi (gemtext) file to write to, otherwise stdout (default)
``` ```

View file

@ -80,7 +80,7 @@ func normal(m *fsm, data []byte) stateFn {
} }
if needsFence(data) { if needsFence(data) {
m.out <- []byte("```\n") m.out <- []byte("```\n")
m.out <- append(data, '\n') m.out <- append(data[4:], '\n')
m.pending = []byte("```") m.pending = []byte("```")
return toFence return toFence
} }
@ -105,7 +105,7 @@ func fence(m *fsm, data []byte) stateFn {
} }
func toFence(m *fsm, data []byte) stateFn { func toFence(m *fsm, data []byte) stateFn {
m.out <- append(data, '\n') m.out <- append(data[4:], '\n')
if needsFence(data) { if needsFence(data) {
return toFence return toFence
} }

66
proc.go
View file

@ -1,17 +1,14 @@
package main package main
// state function import (
type stateFn2 func(*proc, []byte) stateFn2 "bytes"
"fmt"
"regexp"
)
// state machine // state machine
type proc struct { type proc struct {
state stateFn2 out chan []byte
out chan []byte
// combining multiple input lines
buffer []byte
// if we have a termination rule to abide, e.g. implied code fences
pending []byte
} }
func NewProc() *proc { func NewProc() *proc {
@ -21,32 +18,43 @@ func NewProc() *proc {
func (m *proc) Process(in chan []byte) chan []byte { func (m *proc) Process(in chan []byte) chan []byte {
m.out = make(chan []byte) m.out = make(chan []byte)
go func() { go func() {
for m.state = line; m.state != nil; { for b := range in {
b, ok := <-in m.out <- m.process(b)
if !ok {
m.flush()
close(m.out)
m.state = nil
continue
}
m.state = m.state(m, b)
} }
close(m.out)
}() }()
return m.out return m.out
} }
func (m *proc) flush() { func (m *proc) process(data []byte) []byte {
if len(m.pending) > 0 { // find link name and url
m.out <- append(m.pending, '\n') var buffer []byte
m.pending = m.pending[:0] re := regexp.MustCompile(`\[([^\]*]*)\]\(([^)]*)\)`)
for i, match := range re.FindAllSubmatch(data, -1) {
replaceWithIndex := append(match[1], fmt.Sprintf("[%d]", i+1)...)
data = bytes.Replace(data, match[0], replaceWithIndex, 1)
link := fmt.Sprintf("=> %s %d: %s\n", match[2], i+1, match[1])
buffer = append(buffer, link...)
}
if len(buffer) > 0 {
data = append(data, []byte("\n")...)
data = append(data, buffer...)
} }
}
func line(m *proc, data []byte) stateFn2 { // remove comments
// TODO re2 := regexp.MustCompile(`<!--.*-->`)
// find links data = re2.ReplaceAll(data, []byte{})
// collapse lists
m.out <- data
return line // collapse headings
re3 := regexp.MustCompile(`^[#]{4,}`)
data = re3.ReplaceAll(data, []byte("###"))
// heading without spacing
re4 := regexp.MustCompile(`^(#+)[^# ]`)
sub := re4.FindSubmatch(data)
if len(sub) > 0 {
data = bytes.Replace(data, sub[1], append(sub[1], []byte(" ")...), 1)
}
return data
} }