X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=cmd%2Fmicro%2Fhighlight%2Fhighlighter.go;h=a06f1f473e6016e88e8ade46315eb9c8ea641ada;hb=a517ea45bd60770a46b8072b02ef8d7cc54a8379;hp=988923489bf84a1f8fa65f67680cb5b15666e31c;hpb=9c5ab2afbdbaac89cd00bbec749601571d1c11f2;p=micro.git diff --git a/cmd/micro/highlight/highlighter.go b/cmd/micro/highlight/highlighter.go index 98892348..a06f1f47 100644 --- a/cmd/micro/highlight/highlighter.go +++ b/cmd/micro/highlight/highlighter.go @@ -3,10 +3,21 @@ package highlight import ( "regexp" "strings" - - "github.com/dlclark/regexp2" + "unicode/utf8" ) +// RunePos returns the rune index of a given byte index +// This could cause problems if the byte index is between code points +func runePos(p int, str string) int { + if p < 0 { + return 0 + } + if p >= len(str) { + return utf8.RuneCountInString(str) + } + return utf8.RuneCountInString(str[:p]) +} + func combineLineMatch(src, dst LineMatch) LineMatch { for k, v := range src { if g, ok := dst[k]; ok { @@ -21,7 +32,7 @@ func combineLineMatch(src, dst LineMatch) LineMatch { } // A State represents the region at the end of a line -type State *Region +type State *region // LineStates is an interface for a buffer-like object which can also store the states and matches for every line type LineStates interface { @@ -34,22 +45,22 @@ type LineStates interface { // A Highlighter contains the information needed to highlight a string type Highlighter struct { - lastRegion *Region - def *Def + lastRegion *region + Def *Def } // NewHighlighter returns a new highlighter from the given syntax definition func NewHighlighter(def *Def) *Highlighter { h := new(Highlighter) - h.def = def + h.Def = def return h } // LineMatch represents the syntax highlighting matches for one line. Each index where the coloring is changed is marked with that // color's group (represented as one byte) -type LineMatch map[int]uint8 +type LineMatch map[int]Group -func findIndex(regex *regexp2.Regexp, str []byte, canMatchStart, canMatchEnd bool) []int { +func findIndex(regex *regexp.Regexp, skip *regexp.Regexp, str []rune, canMatchStart, canMatchEnd bool) []int { regexStr := regex.String() if strings.Contains(regexStr, "^") { if !canMatchStart { @@ -61,14 +72,26 @@ func findIndex(regex *regexp2.Regexp, str []byte, canMatchStart, canMatchEnd boo return nil } } - match, _ := regex.FindStringMatch(string(str)) + + var strbytes []byte + if skip != nil { + strbytes = skip.ReplaceAllFunc(strbytes, func(match []byte) []byte { + res := make([]byte, utf8.RuneCount(match)) + return res + }) + } else { + strbytes = []byte(string(str)) + } + + match := regex.FindIndex(strbytes) if match == nil { return nil } - return []int{match.Index, match.Index + match.Length} + // return []int{match.Index, match.Index + match.Length} + return []int{runePos(match[0], string(str)), runePos(match[1], string(str))} } -func findAllIndex(regex *regexp.Regexp, str []byte, canMatchStart, canMatchEnd bool) [][]int { +func findAllIndex(regex *regexp.Regexp, str []rune, canMatchStart, canMatchEnd bool) [][]int { regexStr := regex.String() if strings.Contains(regexStr, "^") { if !canMatchStart { @@ -80,51 +103,57 @@ func findAllIndex(regex *regexp.Regexp, str []byte, canMatchStart, canMatchEnd b return nil } } - return regex.FindAllIndex(str, -1) + matches := regex.FindAllIndex([]byte(string(str)), -1) + for i, m := range matches { + matches[i][0] = runePos(m[0], string(str)) + matches[i][1] = runePos(m[1], string(str)) + } + return matches } -func (h *Highlighter) highlightRegion(highlights LineMatch, start int, canMatchEnd bool, lineNum int, line []byte, region *Region, statesOnly bool) LineMatch { +func (h *Highlighter) highlightRegion(highlights LineMatch, start int, canMatchEnd bool, lineNum int, line []rune, curRegion *region, statesOnly bool) LineMatch { // highlights := make(LineMatch) if start == 0 { if !statesOnly { - highlights[0] = region.group + highlights[0] = curRegion.group } } - loc := findIndex(region.end, line, start == 0, canMatchEnd) + loc := findIndex(curRegion.end, curRegion.skip, line, start == 0, canMatchEnd) if loc != nil { if !statesOnly { - highlights[start+loc[1]-1] = region.group + highlights[start+loc[1]-1] = curRegion.group } - if region.parent == nil { + if curRegion.parent == nil { if !statesOnly { highlights[start+loc[1]] = 0 + h.highlightRegion(highlights, start, false, lineNum, line[:loc[0]], curRegion, statesOnly) } - h.highlightRegion(highlights, start, false, lineNum, line[:loc[0]], region, statesOnly) h.highlightEmptyRegion(highlights, start+loc[1], canMatchEnd, lineNum, line[loc[1]:], statesOnly) return highlights } if !statesOnly { - highlights[start+loc[1]] = region.parent.group + highlights[start+loc[1]] = curRegion.parent.group + h.highlightRegion(highlights, start, false, lineNum, line[:loc[0]], curRegion, statesOnly) } - h.highlightRegion(highlights, start, false, lineNum, line[:loc[0]], region, statesOnly) - h.highlightRegion(highlights, start+loc[1], canMatchEnd, lineNum, line[loc[1]:], region.parent, statesOnly) + h.highlightRegion(highlights, start+loc[1], canMatchEnd, lineNum, line[loc[1]:], curRegion.parent, statesOnly) return highlights } if len(line) == 0 || statesOnly { if canMatchEnd { - h.lastRegion = region + h.lastRegion = curRegion } return highlights } firstLoc := []int{len(line), 0} - var firstRegion *Region - for _, r := range region.rules.regions { - loc := findIndex(r.start, line, start == 0, canMatchEnd) + + var firstRegion *region + for _, r := range curRegion.rules.regions { + loc := findIndex(r.start, nil, line, start == 0, canMatchEnd) if loc != nil { if loc[0] < firstLoc[0] { firstLoc = loc @@ -134,17 +163,17 @@ func (h *Highlighter) highlightRegion(highlights LineMatch, start int, canMatchE } if firstLoc[0] != len(line) { highlights[start+firstLoc[0]] = firstRegion.group - h.highlightRegion(highlights, start, false, lineNum, line[:firstLoc[0]], region, statesOnly) + h.highlightRegion(highlights, start, false, lineNum, line[:firstLoc[0]], curRegion, statesOnly) h.highlightRegion(highlights, start+firstLoc[1], canMatchEnd, lineNum, line[firstLoc[1]:], firstRegion, statesOnly) return highlights } - fullHighlights := make([]uint8, len([]rune(string(line)))) + fullHighlights := make([]Group, len([]rune(string(line)))) for i := 0; i < len(fullHighlights); i++ { - fullHighlights[i] = region.group + fullHighlights[i] = curRegion.group } - for _, p := range region.rules.patterns { + for _, p := range curRegion.rules.patterns { matches := findAllIndex(p.regex, line, start == 0, canMatchEnd) for _, m := range matches { for i := m[0]; i < m[1]; i++ { @@ -154,20 +183,20 @@ func (h *Highlighter) highlightRegion(highlights LineMatch, start int, canMatchE } for i, h := range fullHighlights { if i == 0 || h != fullHighlights[i-1] { - if _, ok := highlights[start+i]; !ok { - highlights[start+i] = h - } + // if _, ok := highlights[start+i]; !ok { + highlights[start+i] = h + // } } } if canMatchEnd { - h.lastRegion = region + h.lastRegion = curRegion } return highlights } -func (h *Highlighter) highlightEmptyRegion(highlights LineMatch, start int, canMatchEnd bool, lineNum int, line []byte, statesOnly bool) LineMatch { +func (h *Highlighter) highlightEmptyRegion(highlights LineMatch, start int, canMatchEnd bool, lineNum int, line []rune, statesOnly bool) LineMatch { if len(line) == 0 { if canMatchEnd { h.lastRegion = nil @@ -176,9 +205,9 @@ func (h *Highlighter) highlightEmptyRegion(highlights LineMatch, start int, canM } firstLoc := []int{len(line), 0} - var firstRegion *Region - for _, r := range h.def.rules.regions { - loc := findIndex(r.start, line, start == 0, canMatchEnd) + var firstRegion *region + for _, r := range h.Def.rules.regions { + loc := findIndex(r.start, nil, line, start == 0, canMatchEnd) if loc != nil { if loc[0] < firstLoc[0] { firstLoc = loc @@ -203,8 +232,8 @@ func (h *Highlighter) highlightEmptyRegion(highlights LineMatch, start int, canM return highlights } - fullHighlights := make([]uint8, len(line)) - for _, p := range h.def.rules.patterns { + fullHighlights := make([]Group, len(line)) + for _, p := range h.Def.rules.patterns { matches := findAllIndex(p.regex, line, start == 0, canMatchEnd) for _, m := range matches { for i := m[0]; i < m[1]; i++ { @@ -214,9 +243,9 @@ func (h *Highlighter) highlightEmptyRegion(highlights LineMatch, start int, canM } for i, h := range fullHighlights { if i == 0 || h != fullHighlights[i-1] { - if _, ok := highlights[start+i]; !ok { - highlights[start+i] = h - } + // if _, ok := highlights[start+i]; !ok { + highlights[start+i] = h + // } } } @@ -236,7 +265,7 @@ func (h *Highlighter) HighlightString(input string) []LineMatch { var lineMatches []LineMatch for i := 0; i < len(lines); i++ { - line := []byte(lines[i]) + line := []rune(lines[i]) highlights := make(LineMatch) if i == 0 || h.lastRegion == nil { @@ -252,7 +281,7 @@ func (h *Highlighter) HighlightString(input string) []LineMatch { // HighlightStates correctly sets all states for the buffer func (h *Highlighter) HighlightStates(input LineStates) { for i := 0; i < input.LinesNum(); i++ { - line := []byte(input.Line(i)) + line := []rune(input.Line(i)) // highlights := make(LineMatch) if i == 0 || h.lastRegion == nil { @@ -276,7 +305,7 @@ func (h *Highlighter) HighlightMatches(input LineStates, startline, endline int) break } - line := []byte(input.Line(i)) + line := []rune(input.Line(i)) highlights := make(LineMatch) var match LineMatch @@ -300,7 +329,7 @@ func (h *Highlighter) ReHighlightStates(input LineStates, startline int) { h.lastRegion = input.State(startline - 1) } for i := startline; i < input.LinesNum(); i++ { - line := []byte(input.Line(i)) + line := []rune(input.Line(i)) // highlights := make(LineMatch) // var match LineMatch @@ -322,7 +351,7 @@ func (h *Highlighter) ReHighlightStates(input LineStates, startline int) { // ReHighlightLine will rehighlight the state and match for a single line func (h *Highlighter) ReHighlightLine(input LineStates, lineN int) { - line := []byte(input.Line(lineN)) + line := []rune(input.Line(lineN)) highlights := make(LineMatch) h.lastRegion = nil