...

Source file src/github.com/alecthomas/chroma/lexers/internal/api.go

Documentation: github.com/alecthomas/chroma/lexers/internal

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

View as plain text