2023-12-13 18:54:32 +01:00
|
|
|
package chroma
|
2019-10-27 17:04:31 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"path/filepath"
|
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2022-07-05 03:51:57 +02:00
|
|
|
var (
|
|
|
|
ignoredSuffixes = [...]string{
|
|
|
|
// Editor backups
|
|
|
|
"~", ".bak", ".old", ".orig",
|
|
|
|
// Debian and derivatives apt/dpkg/ucf backups
|
|
|
|
".dpkg-dist", ".dpkg-old", ".ucf-dist", ".ucf-new", ".ucf-old",
|
|
|
|
// Red Hat and derivatives rpm backups
|
|
|
|
".rpmnew", ".rpmorig", ".rpmsave",
|
|
|
|
// Build system input/template files
|
|
|
|
".in",
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2023-12-13 18:54:32 +01:00
|
|
|
// LexerRegistry is a registry of Lexers.
|
|
|
|
type LexerRegistry struct {
|
|
|
|
Lexers Lexers
|
|
|
|
byName map[string]Lexer
|
|
|
|
byAlias map[string]Lexer
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewLexerRegistry creates a new LexerRegistry of Lexers.
|
|
|
|
func NewLexerRegistry() *LexerRegistry {
|
|
|
|
return &LexerRegistry{
|
|
|
|
byName: map[string]Lexer{},
|
|
|
|
byAlias: map[string]Lexer{},
|
|
|
|
}
|
2019-10-27 17:04:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Names of all lexers, optionally including aliases.
|
2023-12-13 18:54:32 +01:00
|
|
|
func (l *LexerRegistry) Names(withAliases bool) []string {
|
2019-10-27 17:04:31 +01:00
|
|
|
out := []string{}
|
2023-12-13 18:54:32 +01:00
|
|
|
for _, lexer := range l.Lexers {
|
2019-10-27 17:04:31 +01:00
|
|
|
config := lexer.Config()
|
|
|
|
out = append(out, config.Name)
|
|
|
|
if withAliases {
|
|
|
|
out = append(out, config.Aliases...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sort.Strings(out)
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get a Lexer by name, alias or file extension.
|
2023-12-13 18:54:32 +01:00
|
|
|
func (l *LexerRegistry) Get(name string) Lexer {
|
|
|
|
if lexer := l.byName[name]; lexer != nil {
|
2020-05-12 02:12:28 +02:00
|
|
|
return lexer
|
2019-10-27 17:04:31 +01:00
|
|
|
}
|
2023-12-13 18:54:32 +01:00
|
|
|
if lexer := l.byAlias[name]; lexer != nil {
|
2020-05-12 02:12:28 +02:00
|
|
|
return lexer
|
2019-10-27 17:04:31 +01:00
|
|
|
}
|
2023-12-13 18:54:32 +01:00
|
|
|
if lexer := l.byName[strings.ToLower(name)]; lexer != nil {
|
2020-05-12 02:12:28 +02:00
|
|
|
return lexer
|
2019-10-27 17:04:31 +01:00
|
|
|
}
|
2023-12-13 18:54:32 +01:00
|
|
|
if lexer := l.byAlias[strings.ToLower(name)]; lexer != nil {
|
2020-05-12 02:12:28 +02:00
|
|
|
return lexer
|
2019-10-27 17:04:31 +01:00
|
|
|
}
|
2020-05-12 02:12:28 +02:00
|
|
|
|
2023-12-13 18:54:32 +01:00
|
|
|
candidates := PrioritisedLexers{}
|
2019-10-27 17:04:31 +01:00
|
|
|
// Try file extension.
|
2023-12-13 18:54:32 +01:00
|
|
|
if lexer := l.Match("filename." + name); lexer != nil {
|
2019-10-27 17:04:31 +01:00
|
|
|
candidates = append(candidates, lexer)
|
|
|
|
}
|
|
|
|
// Try exact filename.
|
2023-12-13 18:54:32 +01:00
|
|
|
if lexer := l.Match(name); lexer != nil {
|
2019-10-27 17:04:31 +01:00
|
|
|
candidates = append(candidates, lexer)
|
|
|
|
}
|
|
|
|
if len(candidates) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
sort.Sort(candidates)
|
|
|
|
return candidates[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
// MatchMimeType attempts to find a lexer for the given MIME type.
|
2023-12-13 18:54:32 +01:00
|
|
|
func (l *LexerRegistry) MatchMimeType(mimeType string) Lexer {
|
|
|
|
matched := PrioritisedLexers{}
|
|
|
|
for _, l := range l.Lexers {
|
2019-10-27 17:04:31 +01:00
|
|
|
for _, lmt := range l.Config().MimeTypes {
|
|
|
|
if mimeType == lmt {
|
|
|
|
matched = append(matched, l)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(matched) != 0 {
|
|
|
|
sort.Sort(matched)
|
|
|
|
return matched[0]
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Match returns the first lexer matching filename.
|
2023-12-13 18:54:32 +01:00
|
|
|
//
|
|
|
|
// Note that this iterates over all file patterns in all lexers, so is not fast.
|
|
|
|
func (l *LexerRegistry) Match(filename string) Lexer {
|
2019-10-27 17:04:31 +01:00
|
|
|
filename = filepath.Base(filename)
|
2023-12-13 18:54:32 +01:00
|
|
|
matched := PrioritisedLexers{}
|
2019-10-27 17:04:31 +01:00
|
|
|
// First, try primary filename matches.
|
2023-12-13 18:54:32 +01:00
|
|
|
for _, lexer := range l.Lexers {
|
2019-10-27 17:04:31 +01:00
|
|
|
config := lexer.Config()
|
|
|
|
for _, glob := range config.Filenames {
|
2022-07-05 03:51:57 +02:00
|
|
|
ok, err := filepath.Match(glob, filename)
|
|
|
|
if err != nil { // nolint
|
|
|
|
panic(err)
|
|
|
|
} else if ok {
|
2019-10-27 17:04:31 +01:00
|
|
|
matched = append(matched, lexer)
|
2022-07-05 03:51:57 +02:00
|
|
|
} else {
|
|
|
|
for _, suf := range &ignoredSuffixes {
|
|
|
|
ok, err := filepath.Match(glob+suf, filename)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
} else if ok {
|
|
|
|
matched = append(matched, lexer)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2019-10-27 17:04:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(matched) > 0 {
|
|
|
|
sort.Sort(matched)
|
|
|
|
return matched[0]
|
|
|
|
}
|
|
|
|
matched = nil
|
|
|
|
// Next, try filename aliases.
|
2023-12-13 18:54:32 +01:00
|
|
|
for _, lexer := range l.Lexers {
|
2019-10-27 17:04:31 +01:00
|
|
|
config := lexer.Config()
|
|
|
|
for _, glob := range config.AliasFilenames {
|
2022-07-05 03:51:57 +02:00
|
|
|
ok, err := filepath.Match(glob, filename)
|
|
|
|
if err != nil { // nolint
|
|
|
|
panic(err)
|
|
|
|
} else if ok {
|
2019-10-27 17:04:31 +01:00
|
|
|
matched = append(matched, lexer)
|
2022-07-05 03:51:57 +02:00
|
|
|
} else {
|
|
|
|
for _, suf := range &ignoredSuffixes {
|
|
|
|
ok, err := filepath.Match(glob+suf, filename)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
} else if ok {
|
|
|
|
matched = append(matched, lexer)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2019-10-27 17:04:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(matched) > 0 {
|
|
|
|
sort.Sort(matched)
|
|
|
|
return matched[0]
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Analyse text content and return the "best" lexer..
|
2023-12-13 18:54:32 +01:00
|
|
|
func (l *LexerRegistry) Analyse(text string) Lexer {
|
|
|
|
var picked Lexer
|
2019-10-27 17:04:31 +01:00
|
|
|
highest := float32(0.0)
|
2023-12-13 18:54:32 +01:00
|
|
|
for _, lexer := range l.Lexers {
|
|
|
|
if analyser, ok := lexer.(Analyser); ok {
|
2019-10-27 17:04:31 +01:00
|
|
|
weight := analyser.AnalyseText(text)
|
|
|
|
if weight > highest {
|
|
|
|
picked = lexer
|
|
|
|
highest = weight
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return picked
|
|
|
|
}
|
|
|
|
|
2023-12-13 18:54:32 +01:00
|
|
|
// Register a Lexer with the LexerRegistry. If the lexer is already registered
|
|
|
|
// it will be replaced.
|
|
|
|
func (l *LexerRegistry) Register(lexer Lexer) Lexer {
|
|
|
|
lexer.SetRegistry(l)
|
2019-10-27 17:04:31 +01:00
|
|
|
config := lexer.Config()
|
2023-12-13 18:54:32 +01:00
|
|
|
|
|
|
|
l.byName[config.Name] = lexer
|
|
|
|
l.byName[strings.ToLower(config.Name)] = lexer
|
|
|
|
|
2019-10-27 17:04:31 +01:00
|
|
|
for _, alias := range config.Aliases {
|
2023-12-13 18:54:32 +01:00
|
|
|
l.byAlias[alias] = lexer
|
|
|
|
l.byAlias[strings.ToLower(alias)] = lexer
|
2019-10-27 17:04:31 +01:00
|
|
|
}
|
2023-12-13 18:54:32 +01:00
|
|
|
|
|
|
|
l.Lexers = add(l.Lexers, lexer)
|
|
|
|
|
2019-10-27 17:04:31 +01:00
|
|
|
return lexer
|
|
|
|
}
|
|
|
|
|
2023-12-13 18:54:32 +01:00
|
|
|
// add adds a lexer to a slice of lexers if it doesn't already exist, or if found will replace it.
|
|
|
|
func add(lexers Lexers, lexer Lexer) Lexers {
|
|
|
|
for i, val := range lexers {
|
|
|
|
if val == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if val.Config().Name == lexer.Config().Name {
|
|
|
|
lexers[i] = lexer
|
|
|
|
return lexers
|
|
|
|
}
|
2021-04-28 18:35:32 +02:00
|
|
|
}
|
2019-10-27 17:04:31 +01:00
|
|
|
|
2023-12-13 18:54:32 +01:00
|
|
|
return append(lexers, lexer)
|
|
|
|
}
|