Stanislav N. aka pztrn
48d43ca097
Pagination now works. Temporary hardcoded 10 pastes per page, will be put in configuration later. Maybe. From now user will receive readable error message if error occured. Started to work on syntax highlighting, tried to make lexers detection work but apparently to no avail.
178 lines
4.0 KiB
Go
178 lines
4.0 KiB
Go
package regexp2
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
|
|
"github.com/dlclark/regexp2/syntax"
|
|
)
|
|
|
|
const (
|
|
replaceSpecials = 4
|
|
replaceLeftPortion = -1
|
|
replaceRightPortion = -2
|
|
replaceLastGroup = -3
|
|
replaceWholeString = -4
|
|
)
|
|
|
|
// MatchEvaluator is a function that takes a match and returns a replacement string to be used
|
|
type MatchEvaluator func(Match) string
|
|
|
|
// Three very similar algorithms appear below: replace (pattern),
|
|
// replace (evaluator), and split.
|
|
|
|
// Replace Replaces all occurrences of the regex in the string with the
|
|
// replacement pattern.
|
|
//
|
|
// Note that the special case of no matches is handled on its own:
|
|
// with no matches, the input string is returned unchanged.
|
|
// The right-to-left case is split out because StringBuilder
|
|
// doesn't handle right-to-left string building directly very well.
|
|
func replace(regex *Regexp, data *syntax.ReplacerData, evaluator MatchEvaluator, input string, startAt, count int) (string, error) {
|
|
if count < -1 {
|
|
return "", errors.New("Count too small")
|
|
}
|
|
if count == 0 {
|
|
return "", nil
|
|
}
|
|
|
|
m, err := regex.FindStringMatchStartingAt(input, startAt)
|
|
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if m == nil {
|
|
return input, nil
|
|
}
|
|
|
|
buf := &bytes.Buffer{}
|
|
text := m.text
|
|
|
|
if !regex.RightToLeft() {
|
|
prevat := 0
|
|
for m != nil {
|
|
if m.Index != prevat {
|
|
buf.WriteString(string(text[prevat:m.Index]))
|
|
}
|
|
prevat = m.Index + m.Length
|
|
if evaluator == nil {
|
|
replacementImpl(data, buf, m)
|
|
} else {
|
|
buf.WriteString(evaluator(*m))
|
|
}
|
|
|
|
count--
|
|
if count == 0 {
|
|
break
|
|
}
|
|
m, err = regex.FindNextMatch(m)
|
|
if err != nil {
|
|
return "", nil
|
|
}
|
|
}
|
|
|
|
if prevat < len(text) {
|
|
buf.WriteString(string(text[prevat:]))
|
|
}
|
|
} else {
|
|
prevat := len(text)
|
|
var al []string
|
|
|
|
for m != nil {
|
|
if m.Index+m.Length != prevat {
|
|
al = append(al, string(text[m.Index+m.Length:prevat]))
|
|
}
|
|
prevat = m.Index
|
|
if evaluator == nil {
|
|
replacementImplRTL(data, &al, m)
|
|
} else {
|
|
al = append(al, evaluator(*m))
|
|
}
|
|
|
|
count--
|
|
if count == 0 {
|
|
break
|
|
}
|
|
m, err = regex.FindNextMatch(m)
|
|
if err != nil {
|
|
return "", nil
|
|
}
|
|
}
|
|
|
|
if prevat > 0 {
|
|
buf.WriteString(string(text[:prevat]))
|
|
}
|
|
|
|
for i := len(al) - 1; i >= 0; i-- {
|
|
buf.WriteString(al[i])
|
|
}
|
|
}
|
|
|
|
return buf.String(), nil
|
|
}
|
|
|
|
// Given a Match, emits into the StringBuilder the evaluated
|
|
// substitution pattern.
|
|
func replacementImpl(data *syntax.ReplacerData, buf *bytes.Buffer, m *Match) {
|
|
for _, r := range data.Rules {
|
|
|
|
if r >= 0 { // string lookup
|
|
buf.WriteString(data.Strings[r])
|
|
} else if r < -replaceSpecials { // group lookup
|
|
m.groupValueAppendToBuf(-replaceSpecials-1-r, buf)
|
|
} else {
|
|
switch -replaceSpecials - 1 - r { // special insertion patterns
|
|
case replaceLeftPortion:
|
|
for i := 0; i < m.Index; i++ {
|
|
buf.WriteRune(m.text[i])
|
|
}
|
|
case replaceRightPortion:
|
|
for i := m.Index + m.Length; i < len(m.text); i++ {
|
|
buf.WriteRune(m.text[i])
|
|
}
|
|
case replaceLastGroup:
|
|
m.groupValueAppendToBuf(m.GroupCount()-1, buf)
|
|
case replaceWholeString:
|
|
for i := 0; i < len(m.text); i++ {
|
|
buf.WriteRune(m.text[i])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func replacementImplRTL(data *syntax.ReplacerData, al *[]string, m *Match) {
|
|
l := *al
|
|
buf := &bytes.Buffer{}
|
|
|
|
for _, r := range data.Rules {
|
|
buf.Reset()
|
|
if r >= 0 { // string lookup
|
|
l = append(l, data.Strings[r])
|
|
} else if r < -replaceSpecials { // group lookup
|
|
m.groupValueAppendToBuf(-replaceSpecials-1-r, buf)
|
|
l = append(l, buf.String())
|
|
} else {
|
|
switch -replaceSpecials - 1 - r { // special insertion patterns
|
|
case replaceLeftPortion:
|
|
for i := 0; i < m.Index; i++ {
|
|
buf.WriteRune(m.text[i])
|
|
}
|
|
case replaceRightPortion:
|
|
for i := m.Index + m.Length; i < len(m.text); i++ {
|
|
buf.WriteRune(m.text[i])
|
|
}
|
|
case replaceLastGroup:
|
|
m.groupValueAppendToBuf(m.GroupCount()-1, buf)
|
|
case replaceWholeString:
|
|
for i := 0; i < len(m.text); i++ {
|
|
buf.WriteRune(m.text[i])
|
|
}
|
|
}
|
|
l = append(l, buf.String())
|
|
}
|
|
}
|
|
|
|
*al = l
|
|
}
|