1// Package internal contains common API functions and structures shared between lexer packages. 2package internal 3 4import ( 5 "path/filepath" 6 "sort" 7 "strings" 8 9 "github.com/danwakefield/fnmatch" 10 11 "github.com/alecthomas/chroma" 12) 13 14// Registry of Lexers. 15var Registry = struct { 16 Lexers chroma.Lexers 17 byName map[string]chroma.Lexer 18 byAlias map[string]chroma.Lexer 19}{ 20 byName: map[string]chroma.Lexer{}, 21 byAlias: map[string]chroma.Lexer{}, 22} 23 24// Names of all lexers, optionally including aliases. 25func Names(withAliases bool) []string { 26 out := []string{} 27 for _, lexer := range Registry.Lexers { 28 config := lexer.Config() 29 out = append(out, config.Name) 30 if withAliases { 31 out = append(out, config.Aliases...) 32 } 33 } 34 sort.Strings(out) 35 return out 36} 37 38// Get a Lexer by name, alias or file extension. 39func Get(name string) chroma.Lexer { 40 if lexer := Registry.byName[name]; lexer != nil { 41 return lexer 42 } 43 if lexer := Registry.byAlias[name]; lexer != nil { 44 return lexer 45 } 46 if lexer := Registry.byName[strings.ToLower(name)]; lexer != nil { 47 return lexer 48 } 49 if lexer := Registry.byAlias[strings.ToLower(name)]; lexer != nil { 50 return lexer 51 } 52 53 candidates := chroma.PrioritisedLexers{} 54 // Try file extension. 55 if lexer := Match("filename." + name); lexer != nil { 56 candidates = append(candidates, lexer) 57 } 58 // Try exact filename. 59 if lexer := Match(name); lexer != nil { 60 candidates = append(candidates, lexer) 61 } 62 if len(candidates) == 0 { 63 return nil 64 } 65 sort.Sort(candidates) 66 return candidates[0] 67} 68 69// MatchMimeType attempts to find a lexer for the given MIME type. 70func MatchMimeType(mimeType string) chroma.Lexer { 71 matched := chroma.PrioritisedLexers{} 72 for _, l := range Registry.Lexers { 73 for _, lmt := range l.Config().MimeTypes { 74 if mimeType == lmt { 75 matched = append(matched, l) 76 } 77 } 78 } 79 if len(matched) != 0 { 80 sort.Sort(matched) 81 return matched[0] 82 } 83 return nil 84} 85 86// Match returns the first lexer matching filename. 87func Match(filename string) chroma.Lexer { 88 filename = filepath.Base(filename) 89 matched := chroma.PrioritisedLexers{} 90 // First, try primary filename matches. 91 for _, lexer := range Registry.Lexers { 92 config := lexer.Config() 93 for _, glob := range config.Filenames { 94 if fnmatch.Match(glob, filename, 0) { 95 matched = append(matched, lexer) 96 } 97 } 98 } 99 if len(matched) > 0 { 100 sort.Sort(matched) 101 return matched[0] 102 } 103 matched = nil 104 // Next, try filename aliases. 105 for _, lexer := range Registry.Lexers { 106 config := lexer.Config() 107 for _, glob := range config.AliasFilenames { 108 if fnmatch.Match(glob, filename, 0) { 109 matched = append(matched, lexer) 110 } 111 } 112 } 113 if len(matched) > 0 { 114 sort.Sort(matched) 115 return matched[0] 116 } 117 return nil 118} 119 120// Analyse text content and return the "best" lexer.. 121func Analyse(text string) chroma.Lexer { 122 var picked chroma.Lexer 123 highest := float32(0.0) 124 for _, lexer := range Registry.Lexers { 125 if analyser, ok := lexer.(chroma.Analyser); ok { 126 weight := analyser.AnalyseText(text) 127 if weight > highest { 128 picked = lexer 129 highest = weight 130 } 131 } 132 } 133 return picked 134} 135 136// Register a Lexer with the global registry. 137func Register(lexer chroma.Lexer) chroma.Lexer { 138 config := lexer.Config() 139 Registry.byName[config.Name] = lexer 140 Registry.byName[strings.ToLower(config.Name)] = lexer 141 for _, alias := range config.Aliases { 142 Registry.byAlias[alias] = lexer 143 Registry.byAlias[strings.ToLower(alias)] = lexer 144 } 145 Registry.Lexers = append(Registry.Lexers, lexer) 146 return lexer 147} 148 149// PlaintextRules is used for the fallback lexer as well as the explicit 150// plaintext lexer. 151func PlaintextRules() chroma.Rules { 152 return chroma.Rules{ 153 "root": []chroma.Rule{ 154 {`.+`, chroma.Text, nil}, 155 {`\n`, chroma.Text, nil}, 156 }, 157 } 158} 159 160// Fallback lexer if no other is found. 161var Fallback chroma.Lexer = chroma.MustNewLazyLexer(&chroma.Config{ 162 Name: "fallback", 163 Filenames: []string{"*"}, 164}, PlaintextRules) 165