1. package chroma
    
  2. 
    
  3. import (
    
  4. 	"path/filepath"
    
  5. 	"sort"
    
  6. 	"strings"
    
  7. )
    
  8. 
    
  9. var (
    
  10. 	ignoredSuffixes = [...]string{
    
  11. 		// Editor backups
    
  12. 		"~", ".bak", ".old", ".orig",
    
  13. 		// Debian and derivatives apt/dpkg/ucf backups
    
  14. 		".dpkg-dist", ".dpkg-old", ".ucf-dist", ".ucf-new", ".ucf-old",
    
  15. 		// Red Hat and derivatives rpm backups
    
  16. 		".rpmnew", ".rpmorig", ".rpmsave",
    
  17. 		// Build system input/template files
    
  18. 		".in",
    
  19. 	}
    
  20. )
    
  21. 
    
  22. // LexerRegistry is a registry of Lexers.
    
  23. type LexerRegistry struct {
    
  24. 	Lexers  Lexers
    
  25. 	byName  map[string]Lexer
    
  26. 	byAlias map[string]Lexer
    
  27. }
    
  28. 
    
  29. // NewLexerRegistry creates a new LexerRegistry of Lexers.
    
  30. func NewLexerRegistry() *LexerRegistry {
    
  31. 	return &LexerRegistry{
    
  32. 		byName:  map[string]Lexer{},
    
  33. 		byAlias: map[string]Lexer{},
    
  34. 	}
    
  35. }
    
  36. 
    
  37. // Names of all lexers, optionally including aliases.
    
  38. func (l *LexerRegistry) Names(withAliases bool) []string {
    
  39. 	out := []string{}
    
  40. 	for _, lexer := range l.Lexers {
    
  41. 		config := lexer.Config()
    
  42. 		out = append(out, config.Name)
    
  43. 		if withAliases {
    
  44. 			out = append(out, config.Aliases...)
    
  45. 		}
    
  46. 	}
    
  47. 	sort.Strings(out)
    
  48. 	return out
    
  49. }
    
  50. 
    
  51. // Get a Lexer by name, alias or file extension.
    
  52. func (l *LexerRegistry) Get(name string) Lexer {
    
  53. 	if lexer := l.byName[name]; lexer != nil {
    
  54. 		return lexer
    
  55. 	}
    
  56. 	if lexer := l.byAlias[name]; lexer != nil {
    
  57. 		return lexer
    
  58. 	}
    
  59. 	if lexer := l.byName[strings.ToLower(name)]; lexer != nil {
    
  60. 		return lexer
    
  61. 	}
    
  62. 	if lexer := l.byAlias[strings.ToLower(name)]; lexer != nil {
    
  63. 		return lexer
    
  64. 	}
    
  65. 
    
  66. 	candidates := PrioritisedLexers{}
    
  67. 	// Try file extension.
    
  68. 	if lexer := l.Match("filename." + name); lexer != nil {
    
  69. 		candidates = append(candidates, lexer)
    
  70. 	}
    
  71. 	// Try exact filename.
    
  72. 	if lexer := l.Match(name); lexer != nil {
    
  73. 		candidates = append(candidates, lexer)
    
  74. 	}
    
  75. 	if len(candidates) == 0 {
    
  76. 		return nil
    
  77. 	}
    
  78. 	sort.Sort(candidates)
    
  79. 	return candidates[0]
    
  80. }
    
  81. 
    
  82. // MatchMimeType attempts to find a lexer for the given MIME type.
    
  83. func (l *LexerRegistry) MatchMimeType(mimeType string) Lexer {
    
  84. 	matched := PrioritisedLexers{}
    
  85. 	for _, l := range l.Lexers {
    
  86. 		for _, lmt := range l.Config().MimeTypes {
    
  87. 			if mimeType == lmt {
    
  88. 				matched = append(matched, l)
    
  89. 			}
    
  90. 		}
    
  91. 	}
    
  92. 	if len(matched) != 0 {
    
  93. 		sort.Sort(matched)
    
  94. 		return matched[0]
    
  95. 	}
    
  96. 	return nil
    
  97. }
    
  98. 
    
  99. // Match returns the first lexer matching filename.
    
  100. //
    
  101. // Note that this iterates over all file patterns in all lexers, so is not fast.
    
  102. func (l *LexerRegistry) Match(filename string) Lexer {
    
  103. 	filename = filepath.Base(filename)
    
  104. 	matched := PrioritisedLexers{}
    
  105. 	// First, try primary filename matches.
    
  106. 	for _, lexer := range l.Lexers {
    
  107. 		config := lexer.Config()
    
  108. 		for _, glob := range config.Filenames {
    
  109. 			ok, err := filepath.Match(glob, filename)
    
  110. 			if err != nil { // nolint
    
  111. 				panic(err)
    
  112. 			} else if ok {
    
  113. 				matched = append(matched, lexer)
    
  114. 			} else {
    
  115. 				for _, suf := range &ignoredSuffixes {
    
  116. 					ok, err := filepath.Match(glob+suf, filename)
    
  117. 					if err != nil {
    
  118. 						panic(err)
    
  119. 					} else if ok {
    
  120. 						matched = append(matched, lexer)
    
  121. 						break
    
  122. 					}
    
  123. 				}
    
  124. 			}
    
  125. 		}
    
  126. 	}
    
  127. 	if len(matched) > 0 {
    
  128. 		sort.Sort(matched)
    
  129. 		return matched[0]
    
  130. 	}
    
  131. 	matched = nil
    
  132. 	// Next, try filename aliases.
    
  133. 	for _, lexer := range l.Lexers {
    
  134. 		config := lexer.Config()
    
  135. 		for _, glob := range config.AliasFilenames {
    
  136. 			ok, err := filepath.Match(glob, filename)
    
  137. 			if err != nil { // nolint
    
  138. 				panic(err)
    
  139. 			} else if ok {
    
  140. 				matched = append(matched, lexer)
    
  141. 			} else {
    
  142. 				for _, suf := range &ignoredSuffixes {
    
  143. 					ok, err := filepath.Match(glob+suf, filename)
    
  144. 					if err != nil {
    
  145. 						panic(err)
    
  146. 					} else if ok {
    
  147. 						matched = append(matched, lexer)
    
  148. 						break
    
  149. 					}
    
  150. 				}
    
  151. 			}
    
  152. 		}
    
  153. 	}
    
  154. 	if len(matched) > 0 {
    
  155. 		sort.Sort(matched)
    
  156. 		return matched[0]
    
  157. 	}
    
  158. 	return nil
    
  159. }
    
  160. 
    
  161. // Analyse text content and return the "best" lexer..
    
  162. func (l *LexerRegistry) Analyse(text string) Lexer {
    
  163. 	var picked Lexer
    
  164. 	highest := float32(0.0)
    
  165. 	for _, lexer := range l.Lexers {
    
  166. 		if analyser, ok := lexer.(Analyser); ok {
    
  167. 			weight := analyser.AnalyseText(text)
    
  168. 			if weight > highest {
    
  169. 				picked = lexer
    
  170. 				highest = weight
    
  171. 			}
    
  172. 		}
    
  173. 	}
    
  174. 	return picked
    
  175. }
    
  176. 
    
  177. // Register a Lexer with the LexerRegistry. If the lexer is already registered
    
  178. // it will be replaced.
    
  179. func (l *LexerRegistry) Register(lexer Lexer) Lexer {
    
  180. 	lexer.SetRegistry(l)
    
  181. 	config := lexer.Config()
    
  182. 
    
  183. 	l.byName[config.Name] = lexer
    
  184. 	l.byName[strings.ToLower(config.Name)] = lexer
    
  185. 
    
  186. 	for _, alias := range config.Aliases {
    
  187. 		l.byAlias[alias] = lexer
    
  188. 		l.byAlias[strings.ToLower(alias)] = lexer
    
  189. 	}
    
  190. 
    
  191. 	l.Lexers = add(l.Lexers, lexer)
    
  192. 
    
  193. 	return lexer
    
  194. }
    
  195. 
    
  196. // add adds a lexer to a slice of lexers if it doesn't already exist, or if found will replace it.
    
  197. func add(lexers Lexers, lexer Lexer) Lexers {
    
  198. 	for i, val := range lexers {
    
  199. 		if val == nil {
    
  200. 			continue
    
  201. 		}
    
  202. 
    
  203. 		if val.Config().Name == lexer.Config().Name {
    
  204. 			lexers[i] = lexer
    
  205. 			return lexers
    
  206. 		}
    
  207. 	}
    
  208. 
    
  209. 	return append(lexers, lexer)
    
  210. }