a61616d79e
This change introduces header files for syntax files. The header files only contain the filetype and detection info and can be parsed much faster than parsing a full yaml file. To determine which filetype a file is, only scanning the headers is necessary and afterwards only one yaml file needs to be parsed. Use the make_headers.go file to generate the header files. Micro expects that all default syntax files will have header files and that custom user syntax files may or may not have them. Resolving includes within syntax has not yet been implemented. This optimization improves startup time. Ref #1427
287 lines
7.9 KiB
Go
287 lines
7.9 KiB
Go
package config
|
|
|
|
import (
|
|
"errors"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
RTColorscheme = 0
|
|
RTSyntax = 1
|
|
RTHelp = 2
|
|
RTPlugin = 3
|
|
RTSyntaxHeader = 4
|
|
NumTypes = 5 // How many filetypes are there
|
|
)
|
|
|
|
type RTFiletype byte
|
|
|
|
// RuntimeFile allows the program to read runtime data like colorschemes or syntax files
|
|
type RuntimeFile interface {
|
|
// Name returns a name of the file without paths or extensions
|
|
Name() string
|
|
// Data returns the content of the file.
|
|
Data() ([]byte, error)
|
|
}
|
|
|
|
// allFiles contains all available files, mapped by filetype
|
|
var allFiles [NumTypes][]RuntimeFile
|
|
var realFiles [NumTypes][]RuntimeFile
|
|
|
|
// some file on filesystem
|
|
type realFile string
|
|
|
|
// some asset file
|
|
type assetFile string
|
|
|
|
// some file on filesystem but with a different name
|
|
type namedFile struct {
|
|
realFile
|
|
name string
|
|
}
|
|
|
|
// a file with the data stored in memory
|
|
type memoryFile struct {
|
|
name string
|
|
data []byte
|
|
}
|
|
|
|
func (mf memoryFile) Name() string {
|
|
return mf.name
|
|
}
|
|
func (mf memoryFile) Data() ([]byte, error) {
|
|
return mf.data, nil
|
|
}
|
|
|
|
func (rf realFile) Name() string {
|
|
fn := filepath.Base(string(rf))
|
|
return fn[:len(fn)-len(filepath.Ext(fn))]
|
|
}
|
|
|
|
func (rf realFile) Data() ([]byte, error) {
|
|
return ioutil.ReadFile(string(rf))
|
|
}
|
|
|
|
func (af assetFile) Name() string {
|
|
fn := path.Base(string(af))
|
|
return fn[:len(fn)-len(path.Ext(fn))]
|
|
}
|
|
|
|
func (af assetFile) Data() ([]byte, error) {
|
|
return Asset(string(af))
|
|
}
|
|
|
|
func (nf namedFile) Name() string {
|
|
return nf.name
|
|
}
|
|
|
|
// AddRuntimeFile registers a file for the given filetype
|
|
func AddRuntimeFile(fileType RTFiletype, file RuntimeFile) {
|
|
allFiles[fileType] = append(allFiles[fileType], file)
|
|
}
|
|
|
|
// AddRealRuntimeFile registers a file for the given filetype
|
|
func AddRealRuntimeFile(fileType RTFiletype, file RuntimeFile) {
|
|
allFiles[fileType] = append(allFiles[fileType], file)
|
|
realFiles[fileType] = append(realFiles[fileType], file)
|
|
}
|
|
|
|
// AddRuntimeFilesFromDirectory registers each file from the given directory for
|
|
// the filetype which matches the file-pattern
|
|
func AddRuntimeFilesFromDirectory(fileType RTFiletype, directory, pattern string) {
|
|
files, _ := ioutil.ReadDir(directory)
|
|
for _, f := range files {
|
|
if ok, _ := filepath.Match(pattern, f.Name()); !f.IsDir() && ok {
|
|
fullPath := filepath.Join(directory, f.Name())
|
|
AddRealRuntimeFile(fileType, realFile(fullPath))
|
|
}
|
|
}
|
|
}
|
|
|
|
// AddRuntimeFilesFromAssets registers each file from the given asset-directory for
|
|
// the filetype which matches the file-pattern
|
|
func AddRuntimeFilesFromAssets(fileType RTFiletype, directory, pattern string) {
|
|
files, err := AssetDir(directory)
|
|
if err != nil {
|
|
return
|
|
}
|
|
for _, f := range files {
|
|
if ok, _ := path.Match(pattern, f); ok {
|
|
AddRuntimeFile(fileType, assetFile(path.Join(directory, f)))
|
|
}
|
|
}
|
|
}
|
|
|
|
// FindRuntimeFile finds a runtime file of the given filetype and name
|
|
// will return nil if no file was found
|
|
func FindRuntimeFile(fileType RTFiletype, name string) RuntimeFile {
|
|
for _, f := range ListRuntimeFiles(fileType) {
|
|
if f.Name() == name {
|
|
return f
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ListRuntimeFiles lists all known runtime files for the given filetype
|
|
func ListRuntimeFiles(fileType RTFiletype) []RuntimeFile {
|
|
return allFiles[fileType]
|
|
}
|
|
|
|
// ListRealRuntimeFiles lists all real runtime files (on disk) for a filetype
|
|
// these runtime files will be ones defined by the user and loaded from the config directory
|
|
func ListRealRuntimeFiles(fileType RTFiletype) []RuntimeFile {
|
|
return realFiles[fileType]
|
|
}
|
|
|
|
// InitRuntimeFiles initializes all assets file and the config directory
|
|
func InitRuntimeFiles() {
|
|
add := func(fileType RTFiletype, dir, pattern string) {
|
|
AddRuntimeFilesFromDirectory(fileType, filepath.Join(ConfigDir, dir), pattern)
|
|
AddRuntimeFilesFromAssets(fileType, path.Join("runtime", dir), pattern)
|
|
}
|
|
|
|
add(RTColorscheme, "colorschemes", "*.micro")
|
|
add(RTSyntax, "syntax", "*.yaml")
|
|
add(RTSyntaxHeader, "syntax", "*.hdr")
|
|
add(RTHelp, "help", "*.md")
|
|
|
|
initlua := filepath.Join(ConfigDir, "init.lua")
|
|
if _, err := os.Stat(initlua); !os.IsNotExist(err) {
|
|
p := new(Plugin)
|
|
p.Name = "initlua"
|
|
p.DirName = "initlua"
|
|
p.Srcs = append(p.Srcs, realFile(initlua))
|
|
Plugins = append(Plugins, p)
|
|
}
|
|
|
|
// Search ConfigDir for plugin-scripts
|
|
plugdir := filepath.Join(ConfigDir, "plug")
|
|
files, _ := ioutil.ReadDir(plugdir)
|
|
|
|
isID := regexp.MustCompile(`^[_A-Za-z0-9]+$`).MatchString
|
|
|
|
for _, d := range files {
|
|
if d.IsDir() {
|
|
srcs, _ := ioutil.ReadDir(filepath.Join(plugdir, d.Name()))
|
|
p := new(Plugin)
|
|
p.Name = d.Name()
|
|
p.DirName = d.Name()
|
|
for _, f := range srcs {
|
|
if strings.HasSuffix(f.Name(), ".lua") {
|
|
p.Srcs = append(p.Srcs, realFile(filepath.Join(plugdir, d.Name(), f.Name())))
|
|
} else if f.Name() == "info.json" {
|
|
data, err := ioutil.ReadFile(filepath.Join(plugdir, d.Name(), "info.json"))
|
|
if err != nil {
|
|
continue
|
|
}
|
|
p.Info, _ = NewPluginInfo(data)
|
|
p.Name = p.Info.Name
|
|
}
|
|
}
|
|
|
|
if !isID(p.Name) {
|
|
log.Println("Invalid plugin name", p.Name)
|
|
continue
|
|
}
|
|
Plugins = append(Plugins, p)
|
|
}
|
|
}
|
|
|
|
plugdir = filepath.Join("runtime", "plugins")
|
|
if files, err := AssetDir(plugdir); err == nil {
|
|
for _, d := range files {
|
|
if srcs, err := AssetDir(filepath.Join(plugdir, d)); err == nil {
|
|
p := new(Plugin)
|
|
p.Name = d
|
|
p.DirName = d
|
|
p.Default = true
|
|
for _, f := range srcs {
|
|
if strings.HasSuffix(f, ".lua") {
|
|
p.Srcs = append(p.Srcs, assetFile(filepath.Join(plugdir, d, f)))
|
|
} else if f == "info.json" {
|
|
data, err := Asset(filepath.Join(plugdir, d, "info.json"))
|
|
if err != nil {
|
|
continue
|
|
}
|
|
p.Info, _ = NewPluginInfo(data)
|
|
p.Name = p.Info.Name
|
|
}
|
|
}
|
|
if !isID(p.Name) {
|
|
log.Println("Invalid plugin name", p.Name)
|
|
continue
|
|
}
|
|
Plugins = append(Plugins, p)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// PluginReadRuntimeFile allows plugin scripts to read the content of a runtime file
|
|
func PluginReadRuntimeFile(fileType RTFiletype, name string) string {
|
|
if file := FindRuntimeFile(fileType, name); file != nil {
|
|
if data, err := file.Data(); err == nil {
|
|
return string(data)
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// PluginListRuntimeFiles allows plugins to lists all runtime files of the given type
|
|
func PluginListRuntimeFiles(fileType RTFiletype) []string {
|
|
files := ListRuntimeFiles(fileType)
|
|
result := make([]string, len(files))
|
|
for i, f := range files {
|
|
result[i] = f.Name()
|
|
}
|
|
return result
|
|
}
|
|
|
|
// PluginAddRuntimeFile adds a file to the runtime files for a plugin
|
|
func PluginAddRuntimeFile(plugin string, filetype RTFiletype, filePath string) error {
|
|
log.Println("PLUGIN ADD:", plugin)
|
|
pl := FindPlugin(plugin)
|
|
if pl == nil {
|
|
return errors.New("Plugin " + plugin + " does not exist")
|
|
}
|
|
pldir := pl.DirName
|
|
log.Println("DIRNAME:", pldir)
|
|
fullpath := filepath.Join(ConfigDir, "plug", pldir, filePath)
|
|
if _, err := os.Stat(fullpath); err == nil {
|
|
AddRuntimeFile(filetype, realFile(fullpath))
|
|
} else {
|
|
fullpath = path.Join("runtime", "plugins", pldir, filePath)
|
|
AddRuntimeFile(filetype, assetFile(fullpath))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// PluginAddRuntimeFilesFromDirectory adds files from a directory to the runtime files for a plugin
|
|
func PluginAddRuntimeFilesFromDirectory(plugin string, filetype RTFiletype, directory, pattern string) error {
|
|
pl := FindPlugin(plugin)
|
|
if pl == nil {
|
|
return errors.New("Plugin " + plugin + " does not exist")
|
|
}
|
|
pldir := pl.DirName
|
|
fullpath := filepath.Join(ConfigDir, "plug", pldir, directory)
|
|
if _, err := os.Stat(fullpath); err == nil {
|
|
AddRuntimeFilesFromDirectory(filetype, fullpath, pattern)
|
|
} else {
|
|
fullpath = path.Join("runtime", "plugins", pldir, directory)
|
|
AddRuntimeFilesFromAssets(filetype, fullpath, pattern)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// PluginAddRuntimeFileFromMemory adds a file to the runtime files for a plugin from a given string
|
|
func PluginAddRuntimeFileFromMemory(filetype RTFiletype, filename, data string) {
|
|
AddRuntimeFile(filetype, memoryFile{filename, []byte(data)})
|
|
}
|