mirror of
https://github.com/zyedidia/micro.git
synced 2026-03-20 07:47:15 +09:00
Add support for lookbehind in region regexes
Use the 'regexp2' library for lookahead and lookbehind in region start and end regular expressions to support things like closing quotes that aren't preceded by backslashes.
This commit is contained in:
@@ -2,11 +2,11 @@ package highlight
|
||||
|
||||
func DetectFiletype(defs []*Def, filename string, firstLine []byte) *Def {
|
||||
for _, d := range defs {
|
||||
if d.ftdetect[0].Match([]byte(filename)) {
|
||||
if isMatch, _ := d.ftdetect[0].MatchString(filename); isMatch {
|
||||
return d
|
||||
}
|
||||
if len(d.ftdetect) > 1 {
|
||||
if d.ftdetect[1].Match(firstLine) {
|
||||
if isMatch, _ := d.ftdetect[1].MatchString(string(firstLine)); isMatch {
|
||||
return d
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package highlight
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/dlclark/regexp2"
|
||||
)
|
||||
|
||||
func combineLineMatch(src, dst LineMatch) LineMatch {
|
||||
@@ -46,7 +48,7 @@ func NewHighlighter(def *Def) *Highlighter {
|
||||
// color's group (represented as one byte)
|
||||
type LineMatch map[int]uint8
|
||||
|
||||
func findIndex(regex *regexp.Regexp, str []byte, canMatchStart, canMatchEnd bool) []int {
|
||||
func findIndex(regex *regexp2.Regexp, str []byte, canMatchStart, canMatchEnd bool) []int {
|
||||
regexStr := regex.String()
|
||||
if strings.Contains(regexStr, "^") {
|
||||
if !canMatchStart {
|
||||
@@ -58,7 +60,11 @@ func findIndex(regex *regexp.Regexp, str []byte, canMatchStart, canMatchEnd bool
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return regex.FindIndex(str)
|
||||
match, _ := regex.FindStringMatch(string(str))
|
||||
if match == nil {
|
||||
return nil
|
||||
}
|
||||
return []int{match.Index, match.Index + match.Length}
|
||||
}
|
||||
|
||||
func findAllIndex(regex *regexp.Regexp, str []byte, canMatchStart, canMatchEnd bool) [][]int {
|
||||
@@ -77,7 +83,7 @@ func findAllIndex(regex *regexp.Regexp, str []byte, canMatchStart, canMatchEnd b
|
||||
}
|
||||
|
||||
func (h *Highlighter) highlightRegion(start int, canMatchEnd bool, lineNum int, line []byte, region *Region) LineMatch {
|
||||
fullHighlights := make([]uint8, len(line))
|
||||
fullHighlights := make([]uint8, len([]rune(string(line))))
|
||||
for i := 0; i < len(fullHighlights); i++ {
|
||||
fullHighlights[i] = region.group
|
||||
}
|
||||
@@ -90,6 +96,7 @@ func (h *Highlighter) highlightRegion(start int, canMatchEnd bool, lineNum int,
|
||||
|
||||
loc := findIndex(region.end, line, start == 0, canMatchEnd)
|
||||
if loc != nil {
|
||||
highlights[start+loc[1]-1] = region.group
|
||||
if region.parent == nil {
|
||||
highlights[start+loc[1]] = 0
|
||||
return combineLineMatch(highlights,
|
||||
|
||||
@@ -4,14 +4,16 @@ import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/dlclark/regexp2"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var groups map[string]uint8
|
||||
var Groups map[string]uint8
|
||||
var numGroups uint8
|
||||
|
||||
func GetGroup(n uint8) string {
|
||||
for k, v := range groups {
|
||||
for k, v := range Groups {
|
||||
if v == n {
|
||||
return k
|
||||
}
|
||||
@@ -25,7 +27,7 @@ func GetGroup(n uint8) string {
|
||||
// Then it has the rules which define how to highlight the file
|
||||
type Def struct {
|
||||
FileType string
|
||||
ftdetect []*regexp.Regexp
|
||||
ftdetect []*regexp2.Regexp
|
||||
rules *Rules
|
||||
}
|
||||
|
||||
@@ -53,13 +55,13 @@ type Rules struct {
|
||||
type Region struct {
|
||||
group uint8
|
||||
parent *Region
|
||||
start *regexp.Regexp
|
||||
end *regexp.Regexp
|
||||
start *regexp2.Regexp
|
||||
end *regexp2.Regexp
|
||||
rules *Rules
|
||||
}
|
||||
|
||||
func init() {
|
||||
groups = make(map[string]uint8)
|
||||
Groups = make(map[string]uint8)
|
||||
}
|
||||
|
||||
// ParseDef parses an input syntax file into a highlight Def
|
||||
@@ -86,7 +88,7 @@ func ParseDef(input []byte) (s *Def, err error) {
|
||||
} else if k == "detect" {
|
||||
ftdetect := v.(map[interface{}]interface{})
|
||||
if len(ftdetect) >= 1 {
|
||||
syntax, err := regexp.Compile(ftdetect["filename"].(string))
|
||||
syntax, err := regexp2.Compile(ftdetect["filename"].(string), 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -94,7 +96,7 @@ func ParseDef(input []byte) (s *Def, err error) {
|
||||
s.ftdetect = append(s.ftdetect, syntax)
|
||||
}
|
||||
if len(ftdetect) >= 2 {
|
||||
header, err := regexp.Compile(ftdetect["header"].(string))
|
||||
header, err := regexp2.Compile(ftdetect["header"].(string), 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -172,11 +174,11 @@ func parseRules(input []interface{}, curRegion *Region) (*Rules, error) {
|
||||
}
|
||||
|
||||
groupStr := group.(string)
|
||||
if _, ok := groups[groupStr]; !ok {
|
||||
if _, ok := Groups[groupStr]; !ok {
|
||||
numGroups++
|
||||
groups[groupStr] = numGroups
|
||||
Groups[groupStr] = numGroups
|
||||
}
|
||||
groupNum := groups[groupStr]
|
||||
groupNum := Groups[groupStr]
|
||||
rules.patterns = append(rules.patterns, &Pattern{groupNum, r})
|
||||
}
|
||||
case map[interface{}]interface{}:
|
||||
@@ -199,21 +201,21 @@ func parseRegion(group string, regionInfo map[interface{}]interface{}, prevRegio
|
||||
var err error
|
||||
|
||||
region := new(Region)
|
||||
if _, ok := groups[group]; !ok {
|
||||
if _, ok := Groups[group]; !ok {
|
||||
numGroups++
|
||||
groups[group] = numGroups
|
||||
Groups[group] = numGroups
|
||||
}
|
||||
groupNum := groups[group]
|
||||
groupNum := Groups[group]
|
||||
region.group = groupNum
|
||||
region.parent = prevRegion
|
||||
|
||||
region.start, err = regexp.Compile(regionInfo["start"].(string))
|
||||
region.start, err = regexp2.Compile(regionInfo["start"].(string), 0)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
region.end, err = regexp.Compile(regionInfo["end"].(string))
|
||||
region.end, err = regexp2.Compile(regionInfo["end"].(string), 0)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user