5 "github.com/gdamore/tcell"
13 var syntaxFiles map[[2]*regexp.Regexp][2]string
15 // LoadSyntaxFiles loads the syntax files from the default directory ~/.micro
16 func LoadSyntaxFiles() {
17 usr, _ := user.Current()
19 LoadSyntaxFilesFromDir(dir + "/.micro")
22 // JoinRule takes a syntax rule (which can be multiple regular expressions)
23 // and joins it into one regular expression by ORing everything together
24 func JoinRule(rule string) string {
25 split := strings.Split(rule, `" "`)
26 joined := strings.Join(split, ")|(")
27 joined = "(" + joined + ")"
31 // LoadSyntaxFilesFromDir loads the syntax files from a specified directory
32 // To load the syntax files, we must fill the `syntaxFiles` map
33 // This involves finding the regex for syntax and if it exists, the regex
34 // for the header. Then we must get the text for the file and the filetype.
35 func LoadSyntaxFilesFromDir(dir string) {
36 syntaxFiles = make(map[[2]*regexp.Regexp][2]string)
37 files, _ := ioutil.ReadDir(dir)
38 for _, f := range files {
39 if filepath.Ext(f.Name()) == ".micro" {
40 text, err := ioutil.ReadFile(dir + "/" + f.Name())
43 fmt.Println("Error loading syntax files:", err)
46 lines := strings.Split(string(text), "\n")
48 syntaxParser := regexp.MustCompile(`syntax "(.*?)"\s+"(.*)"+`)
49 headerParser := regexp.MustCompile(`header "(.*)"`)
51 var syntaxRegex *regexp.Regexp
52 var headerRegex *regexp.Regexp
55 for _, line := range lines {
56 if strings.TrimSpace(line) == "" ||
57 strings.TrimSpace(line)[0] == '#' {
62 if strings.HasPrefix(line, "syntax") {
63 syntaxMatches := syntaxParser.FindSubmatch([]byte(line))
64 if len(syntaxMatches) == 3 {
65 if syntaxRegex != nil {
66 regexes := [2]*regexp.Regexp{syntaxRegex, headerRegex}
67 syntaxFiles[regexes] = [2]string{rules, filetype}
70 filetype = string(syntaxMatches[1])
71 extensions := JoinRule(string(syntaxMatches[2]))
73 syntaxRegex, err = regexp.Compile(extensions)
75 fmt.Println("Regex error:", err)
79 fmt.Println("Syntax statement is not valid:", line)
82 } else if strings.HasPrefix(line, "header") {
83 headerMatches := headerParser.FindSubmatch([]byte(line))
84 if len(headerMatches) == 2 {
85 header := JoinRule(string(headerMatches[1]))
87 headerRegex, err = regexp.Compile(header)
89 fmt.Println("Regex error:", err)
93 fmt.Println("Header statement is not valid:", line)
100 if syntaxRegex != nil {
101 regexes := [2]*regexp.Regexp{syntaxRegex, headerRegex}
102 syntaxFiles[regexes] = [2]string{rules, filetype}
108 // GetRules finds the syntax rules that should be used for the buffer
109 // and returns them. It also returns the filetype of the file
110 func GetRules(buf *Buffer) (string, string) {
111 for r := range syntaxFiles {
112 if r[0] != nil && r[0].MatchString(buf.path) {
113 return syntaxFiles[r][0], syntaxFiles[r][1]
114 } else if r[1] != nil && r[1].MatchString(buf.lines[0]) {
115 return syntaxFiles[r][0], syntaxFiles[r][1]
121 // Match takes a buffer and returns a map specifying how it should be syntax highlighted
122 // The map is from character numbers to styles, so map[3] represents the style change
123 // at the third character in the buffer
124 func Match(rules string, buf *Buffer) map[int]tcell.Style {
125 // rules := strings.TrimSpace(GetRules(buf))
128 lines := strings.Split(rules, "\n")
129 m := make(map[int]tcell.Style)
130 parser := regexp.MustCompile(`color (.*?)\s+"(.*)"`)
131 for _, line := range lines {
132 if strings.TrimSpace(line) == "" ||
133 strings.TrimSpace(line)[0] == '#' ||
134 strings.HasPrefix(line, "syntax") ||
135 strings.HasPrefix(line, "header") {
139 submatch := parser.FindSubmatch([]byte(line))
140 color := string(submatch[1])
141 regex, err := regexp.Compile(string(submatch[2]))
143 // Error with the regex!
146 st := StringToStyle(color)
148 if regex.MatchString(str) {
149 indicies := regex.FindAllStringIndex(str, -1)
150 for _, value := range indicies {
151 for i := value[0] + 1; i < value[1]; i++ {
152 if _, exists := m[i]; exists {
157 if _, exists := m[value[1]]; !exists {
158 m[value[1]] = tcell.StyleDefault
167 // StringToStyle returns a style from a string
168 func StringToStyle(str string) tcell.Style {
171 split := strings.Split(str, ",")
173 fg, bg = split[0], split[1]
178 return tcell.StyleDefault.Foreground(StringToColor(fg)).Background(StringToColor(bg))
181 // StringToColor returns a tcell color from a string representation of a color
182 func StringToColor(str string) tcell.Color {
185 return tcell.ColorBlack
187 return tcell.ColorMaroon
189 return tcell.ColorGreen
191 return tcell.ColorOlive
193 return tcell.ColorNavy
195 return tcell.ColorPurple
197 return tcell.ColorTeal
199 return tcell.ColorSilver
201 return tcell.ColorGray
203 return tcell.ColorRed
205 return tcell.ColorLime
207 return tcell.ColorYellow
209 return tcell.ColorBlue
210 case "brightmagenta":
211 return tcell.ColorFuchsia
213 return tcell.ColorAqua
215 return tcell.ColorWhite
217 return tcell.ColorDefault