1
0
textfmt/escape.go

148 lines
2.7 KiB
Go
Raw Normal View History

2025-10-31 20:24:37 +01:00
package textfmt
2025-11-02 06:27:17 +01:00
import (
"code.squareroundforest.org/arpio/textedit"
"errors"
)
2025-10-31 20:24:37 +01:00
2025-11-02 06:27:17 +01:00
type mdEscapeState struct {
lineStarted bool
numberOnNewLine bool
linkValue bool
linkClosed bool
linkOpen bool
2025-10-31 20:24:37 +01:00
}
2025-11-02 06:27:17 +01:00
func escapeTeletypeEdit(r rune, s struct{}) ([]rune, struct{}) {
if r >= 0x00 && r <= 0x1f && r != '\n' && r != '\t' {
return []rune{0xb7}, s
}
if r >= 0x7f && r <= 0x9f {
return []rune{0xb7}, s
}
return []rune{r}, s
2025-10-31 20:24:37 +01:00
}
2025-11-02 06:27:17 +01:00
func escapeTeletype() wrapper {
return editor(
textedit.Func(
escapeTeletypeEdit,
func(struct{}) []rune { return nil },
),
)
}
func escapeRoffEdit(additional ...string) func(rune, bool) ([]rune, bool) {
const invalidAdditional = "invalid additional escape definition"
if len(additional)%2 != 0 {
panic(errors.New(invalidAdditional))
}
esc := map[rune][]rune{
'\\': []rune("\\\\"),
'\u00a0': []rune("\\~"),
}
2025-11-02 22:15:31 +01:00
for i := 0; i < len(additional); i += 2 {
2025-11-02 06:27:17 +01:00
r := []rune(additional[i])
if len(r) != 1 {
panic(errors.New(invalidAdditional))
2025-10-31 20:24:37 +01:00
}
2025-11-02 06:27:17 +01:00
esc[r[0]] = []rune(additional[i+1])
2025-10-31 20:24:37 +01:00
}
2025-11-02 06:27:17 +01:00
lsEsc := map[rune][]rune{
'.': []rune("\\&."),
'\'': []rune("\\&'"),
}
2025-10-31 20:24:37 +01:00
2025-11-02 06:27:17 +01:00
return func(r rune, lineStarted bool) ([]rune, bool) {
if r == '\n' {
return []rune{'\n'}, false
}
2025-10-31 20:24:37 +01:00
2025-11-02 06:27:17 +01:00
replacement, replace := esc[r]
if replace {
return replacement, true
}
2025-10-31 20:24:37 +01:00
2025-11-02 06:27:17 +01:00
if lineStarted {
return []rune{r}, true
}
2025-10-31 20:24:37 +01:00
2025-11-02 06:27:17 +01:00
replacement, replace = lsEsc[r]
if replace {
return replacement, true
2025-10-31 20:24:37 +01:00
}
2025-11-02 06:27:17 +01:00
return []rune{r}, true
2025-10-31 20:24:37 +01:00
}
}
2025-11-02 06:27:17 +01:00
func escapeRoff(additional ...string) wrapper {
return editor(
textedit.Func(
escapeRoffEdit(additional...),
func(bool) []rune { return nil },
),
)
2025-10-31 20:24:37 +01:00
}
2025-11-02 06:27:17 +01:00
func escapeMarkdownEdit(r rune, s mdEscapeState) ([]rune, mdEscapeState) {
var ret []rune
switch r {
case '\\', '`', '*', '_', '[', ']', '#', '<', '>':
ret = append(ret, '\\', r)
2025-12-08 00:41:08 +01:00
case '+', '-':
switch {
case s.lineStarted:
ret = append(ret, r)
default:
ret = append(ret, '\\', r)
}
case '.':
2025-11-02 06:27:17 +01:00
switch {
case s.numberOnNewLine:
2025-12-08 00:41:08 +01:00
ret = append(ret, '\\', r)
default:
ret = append(ret, r)
}
case '(':
switch {
2025-11-02 06:27:17 +01:00
case s.linkClosed:
2025-12-08 00:41:08 +01:00
ret = append(ret, '\\', r)
default:
ret = append(ret, r)
}
case ')':
switch {
2025-11-02 06:27:17 +01:00
case s.linkValue:
2025-12-08 00:41:08 +01:00
ret = append(ret, '\\', r)
2025-11-02 06:27:17 +01:00
default:
ret = append(ret, r)
}
2025-12-08 00:41:08 +01:00
default:
ret = append(ret, r)
2025-11-02 06:27:17 +01:00
}
s.numberOnNewLine = (!s.lineStarted || s.numberOnNewLine) && r >= '0' && r <= '9'
s.lineStarted = r != '\n'
s.linkValue = s.linkClosed && r == '(' || s.linkValue && r != ')'
s.linkClosed = s.linkOpen && r == ']'
s.linkOpen = !s.linkValue && r == '[' || s.linkOpen && r != ']'
return ret, s
2025-10-31 20:24:37 +01:00
}
2025-12-08 00:41:08 +01:00
func escapeMarkdown(s mdEscapeState) wrapper {
2025-11-02 06:27:17 +01:00
return editor(
2025-12-08 00:41:08 +01:00
textedit.FuncInit(
2025-11-02 06:27:17 +01:00
escapeMarkdownEdit,
func(mdEscapeState) []rune { return nil },
2025-12-08 00:41:08 +01:00
s,
2025-11-02 06:27:17 +01:00
),
)
2025-10-31 20:24:37 +01:00
}