10 // A Group represents a syntax group
13 // Groups contains all of the groups that are defined
14 // You can access them in the map via their string name
15 var Groups map[string]Group
18 // String returns the group name attached to the specific group
19 func (g Group) String() string {
20 for k, v := range Groups {
28 // A Def is a full syntax definition for a language
29 // It has a filetype, information about how to detect the filetype based
30 // on filename or header (the first line of the file)
31 // Then it has the rules which define how to highlight the file
34 ftdetect []*regexp.Regexp
38 // A Pattern is one simple syntax rule
39 // It has a group that the rule belongs to, as well as
40 // the regular expression to match the pattern
46 // rules defines which patterns and regions can be used to highlight
54 // A region is a highlighted region (such as a multiline comment, or a string)
55 // It belongs to a group, and has start and end regular expressions
56 // A region also has rules of its own that only apply when matching inside the
57 // region and also rules from the above region do not match inside this region
58 // Note that a region may contain more regions
69 Groups = make(map[string]Group)
72 // ParseDef parses an input syntax file into a highlight Def
73 func ParseDef(input []byte) (s *Def, err error) {
74 // This is just so if we have an error, we can exit cleanly and return the parse error to the user
76 if e := recover(); e != nil {
81 var rules map[interface{}]interface{}
82 if err = yaml.Unmarshal(input, &rules); err != nil {
88 for k, v := range rules {
90 filetype := v.(string)
93 } else if k == "detect" {
94 ftdetect := v.(map[interface{}]interface{})
95 if len(ftdetect) >= 1 {
96 syntax, err := regexp.Compile(ftdetect["filename"].(string))
101 s.ftdetect = append(s.ftdetect, syntax)
103 if len(ftdetect) >= 2 {
104 header, err := regexp.Compile(ftdetect["header"].(string))
109 s.ftdetect = append(s.ftdetect, header)
111 } else if k == "rules" {
112 inputRules := v.([]interface{})
114 rules, err := parseRules(inputRules, nil)
126 // ResolveIncludes will sort out the rules for including other filetypes
127 // You should call this after parsing all the Defs
128 func ResolveIncludes(defs []*Def) {
129 for _, d := range defs {
130 resolveIncludesInDef(defs, d)
134 func resolveIncludesInDef(defs []*Def, d *Def) {
135 for _, lang := range d.rules.includes {
136 for _, searchDef := range defs {
137 if lang == searchDef.FileType {
138 d.rules.patterns = append(d.rules.patterns, searchDef.rules.patterns...)
139 d.rules.regions = append(d.rules.regions, searchDef.rules.regions...)
143 for _, r := range d.rules.regions {
144 resolveIncludesInRegion(defs, r)
149 func resolveIncludesInRegion(defs []*Def, region *region) {
150 for _, lang := range region.rules.includes {
151 for _, searchDef := range defs {
152 if lang == searchDef.FileType {
153 region.rules.patterns = append(region.rules.patterns, searchDef.rules.patterns...)
154 region.rules.regions = append(region.rules.regions, searchDef.rules.regions...)
158 for _, r := range region.rules.regions {
159 resolveIncludesInRegion(defs, r)
164 func parseRules(input []interface{}, curRegion *region) (*rules, error) {
167 for _, v := range input {
168 rule := v.(map[interface{}]interface{})
169 for k, val := range rule {
172 switch object := val.(type) {
175 rules.includes = append(rules.includes, object)
178 r, err := regexp.Compile(object)
183 groupStr := group.(string)
184 if _, ok := Groups[groupStr]; !ok {
186 Groups[groupStr] = numGroups
188 groupNum := Groups[groupStr]
189 rules.patterns = append(rules.patterns, &pattern{groupNum, r})
191 case map[interface{}]interface{}:
193 region, err := parseRegion(group.(string), object, curRegion)
197 rules.regions = append(rules.regions, region)
199 return nil, fmt.Errorf("Bad type %T", object)
207 func parseRegion(group string, regionInfo map[interface{}]interface{}, prevRegion *region) (*region, error) {
210 region := new(region)
211 if _, ok := Groups[group]; !ok {
213 Groups[group] = numGroups
215 groupNum := Groups[group]
216 region.group = groupNum
217 region.parent = prevRegion
219 region.start, err = regexp.Compile(regionInfo["start"].(string))
225 region.end, err = regexp.Compile(regionInfo["end"].(string))
232 if _, ok := regionInfo["skip"]; ok {
233 region.skip, err = regexp.Compile(regionInfo["skip"].(string))
240 region.rules, err = parseRules(regionInfo["rules"].([]interface{}), region)