From a35af11924598bb2647f7fec9fb6ff031213c820 Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Wed, 23 Mar 2016 16:36:17 -0400 Subject: [PATCH] Cache syntax highlighting rules --- runtime/syntax/arduino.micro | 2 +- runtime/syntax/awk.micro | 2 +- runtime/syntax/c.micro | 2 +- runtime/syntax/d.micro | 2 +- src/buffer.go | 2 +- src/highlighter.go | 92 ++++++++++++++++++++---------------- 6 files changed, 55 insertions(+), 47 deletions(-) diff --git a/runtime/syntax/arduino.micro b/runtime/syntax/arduino.micro index b8a80b00..ada231a2 100644 --- a/runtime/syntax/arduino.micro +++ b/runtime/syntax/arduino.micro @@ -95,7 +95,7 @@ color brightyellow "\b(setup|loop)\b" color brightcyan "^[[:space:]]*#[[:space:]]*(define|include(_next)?|(un|ifn?)def|endif|el(if|se)|if|warning|error|pragma)" ## -color brightmagenta "'([^'\]|(\\["'abfnrtv\\]))'" "'\\(([0-3]?[0-7]{1,2}))'" "'\\x[0-9A-Fa-f]{1,2}'" +color brightmagenta "'([^'\\]|(\\["'abfnrtv\\]))'" "'\\(([0-3]?[0-7]{1,2}))'" "'\\x[0-9A-Fa-f]{1,2}'" ## GCC builtins color cyan "__attribute__[[:space:]]*\(\([^)]*\)\)" "__(aligned|asm|builtin|hidden|inline|packed|restrict|section|typeof|weak)__" diff --git a/runtime/syntax/awk.micro b/runtime/syntax/awk.micro index ad7b8482..0ab4cf96 100644 --- a/runtime/syntax/awk.micro +++ b/runtime/syntax/awk.micro @@ -16,7 +16,7 @@ color brightblue "\b(split|sprintf|strtonum|sub|substr|tolower|toupper)\b" color brightblue "\b(mktime|strftime|systime)\b" color brightblue "\b(and|compl|lshift|or|rshift|xor)\b" color brightblue "\b(bindtextdomain|dcgettext|dcngettext)\b" -color magenta "/.*[^\]/" +color magenta "/.*[^\\]/" color yellow ""(\\.|[^"])*"|'(\\.|[^'])*'" color magenta "\\." color brightblack "(^|[[:space:]])#([^{].*)?$" diff --git a/runtime/syntax/c.micro b/runtime/syntax/c.micro index c96d1b60..54a41737 100644 --- a/runtime/syntax/c.micro +++ b/runtime/syntax/c.micro @@ -9,7 +9,7 @@ color statement "\b(for|if|while|do|else|case|default|switch)\b" color statement "\b(try|throw|catch|operator|new|delete)\b" color statement "\b(goto|continue|break|return)\b" color preproc "^[[:space:]]*#[[:space:]]*(define|include|(un|ifn?)def|endif|el(if|se)|if|warning|error)" -color brightmagenta "'([^'\]|(\\["'abfnrtv\\]))'" "'\\(([0-3]?[0-7]{1,2}))'" "'\\x[0-9A-Fa-f]{1,2}'" +color constant "'([^'\\]|(\\["'abfnrtv\\]))'" "'\\(([0-3]?[0-7]{1,2}))'" "'\\x[0-9A-Fa-f]{1,2}'" ## ## GCC builtins diff --git a/runtime/syntax/d.micro b/runtime/syntax/d.micro index c7ccb603..9853c17f 100644 --- a/runtime/syntax/d.micro +++ b/runtime/syntax/d.micro @@ -77,7 +77,7 @@ color constant "q"\{.*\}"" color constant "q"\[.*\]"" color constant "q"<.*>"" color constant (s) "q"[^({[<"][^"]*$.*?^[^"]+"" -color constant "q"([^({[<"]).*\1"" +color constant "q"([^({[<"]).*"" ### TokenString ### True token strings require nesting, so, again, they can't be implemented accurately here. diff --git a/src/buffer.go b/src/buffer.go index 1a4bb067..92b92db4 100644 --- a/src/buffer.go +++ b/src/buffer.go @@ -27,7 +27,7 @@ type Buffer struct { lines []string // Syntax highlighting rules - rules string + rules []SyntaxRule // File type of the buffer filetype string } diff --git a/src/highlighter.go b/src/highlighter.go index 70fcc394..19adfb47 100644 --- a/src/highlighter.go +++ b/src/highlighter.go @@ -10,7 +10,17 @@ import ( "strings" ) -var syntaxFiles map[[2]*regexp.Regexp][2]string +type FileTypeRules struct { + filetype string + rules []SyntaxRule +} + +type SyntaxRule struct { + regex *regexp.Regexp + style tcell.Style +} + +var syntaxFiles map[[2]*regexp.Regexp]FileTypeRules // LoadSyntaxFiles loads the syntax files from the default directory ~/.micro func LoadSyntaxFiles() { @@ -35,7 +45,7 @@ func JoinRule(rule string) string { func LoadSyntaxFilesFromDir(dir string) { InitColorscheme() - syntaxFiles = make(map[[2]*regexp.Regexp][2]string) + syntaxFiles = make(map[[2]*regexp.Regexp]FileTypeRules) files, _ := ioutil.ReadDir(dir) for _, f := range files { if filepath.Ext(f.Name()) == ".micro" { @@ -49,12 +59,13 @@ func LoadSyntaxFilesFromDir(dir string) { syntaxParser := regexp.MustCompile(`syntax "(.*?)"\s+"(.*)"+`) headerParser := regexp.MustCompile(`header "(.*)"`) + ruleParser := regexp.MustCompile(`color (.*?)\s+(?:\((.*?)\)\s+)?"(.*)"`) var syntaxRegex *regexp.Regexp var headerRegex *regexp.Regexp var filetype string - var rules string - for _, line := range lines { + var rules []SyntaxRule + for lineNum, line := range lines { if strings.TrimSpace(line) == "" || strings.TrimSpace(line)[0] == '#' { // Ignore this line @@ -66,8 +77,9 @@ func LoadSyntaxFilesFromDir(dir string) { if len(syntaxMatches) == 3 { if syntaxRegex != nil { regexes := [2]*regexp.Regexp{syntaxRegex, headerRegex} - syntaxFiles[regexes] = [2]string{rules, filetype} + syntaxFiles[regexes] = FileTypeRules{filetype, rules} } + rules = rules[:0] filetype = string(syntaxMatches[1]) extensions := JoinRule(string(syntaxMatches[2])) @@ -96,12 +108,34 @@ func LoadSyntaxFilesFromDir(dir string) { continue } } else { - rules += line + "\n" + if ruleParser.MatchString(line) { + submatch := ruleParser.FindSubmatch([]byte(line)) + color := string(submatch[1]) + var regexStr string + if len(submatch) == 4 { + regexStr = "(?m" + string(submatch[2]) + ")" + JoinRule(string(submatch[3])) + } else if len(submatch) == 3 { + regexStr = "(?m)" + JoinRule(string(submatch[2])) + } + regex, err := regexp.Compile(regexStr) + if err != nil { + fmt.Println(f.Name(), lineNum, err) + continue + } + + st := tcell.StyleDefault + if _, ok := colorscheme[color]; ok { + st = colorscheme[color] + } else { + st = StringToStyle(color) + } + rules = append(rules, SyntaxRule{regex, st}) + } } } if syntaxRegex != nil { regexes := [2]*regexp.Regexp{syntaxRegex, headerRegex} - syntaxFiles[regexes] = [2]string{rules, filetype} + syntaxFiles[regexes] = FileTypeRules{filetype, rules} } } } @@ -109,22 +143,22 @@ func LoadSyntaxFilesFromDir(dir string) { // GetRules finds the syntax rules that should be used for the buffer // and returns them. It also returns the filetype of the file -func GetRules(buf *Buffer) (string, string) { +func GetRules(buf *Buffer) ([]SyntaxRule, string) { for r := range syntaxFiles { if r[0] != nil && r[0].MatchString(buf.path) { - return syntaxFiles[r][0], syntaxFiles[r][1] + return syntaxFiles[r].rules, syntaxFiles[r].filetype } else if r[1] != nil && r[1].MatchString(buf.lines[0]) { - return syntaxFiles[r][0], syntaxFiles[r][1] + return syntaxFiles[r].rules, syntaxFiles[r].filetype } } - return "", "Unknown" + return nil, "Unknown" } // Match takes a buffer and returns a map specifying how it should be syntax highlighted // The map is from character numbers to styles, so map[3] represents the style change // at the third character in the buffer // Note that this map only stores changes in styles, not each character's style -func Match(rules string, buf *Buffer, v *View) map[int]tcell.Style { +func Match(rules []SyntaxRule, buf *Buffer, v *View) map[int]tcell.Style { start := v.topline - synLinesUp end := v.topline + v.height + synLinesDown if start < 0 { @@ -137,42 +171,16 @@ func Match(rules string, buf *Buffer, v *View) map[int]tcell.Style { startNum := v.cursor.loc + v.cursor.Distance(0, start) toplineNum := v.cursor.loc + v.cursor.Distance(0, v.topline) - lines := strings.Split(rules, "\n") m := make(map[int]tcell.Style) - parser := regexp.MustCompile(`color (.*?)\s+(?:\((.*?)\)\s+)?"(.*)"`) - for _, line := range lines { - if strings.TrimSpace(line) == "" { - // Ignore this line - continue - } - submatch := parser.FindSubmatch([]byte(line)) - color := string(submatch[1]) - var regexStr string - if len(submatch) == 4 { - regexStr = "(?m" + string(submatch[2]) + ")" + JoinRule(string(submatch[3])) - } else if len(submatch) == 3 { - regexStr = "(?m)" + JoinRule(string(submatch[2])) - } - regex, err := regexp.Compile(regexStr) - if err != nil { - // Error with the regex! - continue - } - st := tcell.StyleDefault - if _, ok := colorscheme[color]; ok { - st = colorscheme[color] - } else { - st = StringToStyle(color) - } - - if regex.MatchString(str) { - indicies := regex.FindAllStringIndex(str, -1) + for _, rule := range rules { + if rule.regex.MatchString(str) { + indicies := rule.regex.FindAllStringIndex(str, -1) for _, value := range indicies { value[0] += startNum value[1] += startNum for i := value[0]; i < value[1]; i++ { if i >= toplineNum { - m[i] = st + m[i] = rule.style } } }