1package config 2 3import ( 4 "errors" 5 "io/ioutil" 6 "log" 7 "os" 8 "path" 9 "path/filepath" 10 "regexp" 11 "strings" 12) 13 14const ( 15 RTColorscheme = 0 16 RTSyntax = 1 17 RTHelp = 2 18 RTPlugin = 3 19 RTSyntaxHeader = 4 20) 21 22var ( 23 NumTypes = 5 // How many filetypes are there 24) 25 26type RTFiletype int 27 28// RuntimeFile allows the program to read runtime data like colorschemes or syntax files 29type RuntimeFile interface { 30 // Name returns a name of the file without paths or extensions 31 Name() string 32 // Data returns the content of the file. 33 Data() ([]byte, error) 34} 35 36// allFiles contains all available files, mapped by filetype 37var allFiles [][]RuntimeFile 38var realFiles [][]RuntimeFile 39 40func init() { 41 allFiles = make([][]RuntimeFile, NumTypes) 42 realFiles = make([][]RuntimeFile, NumTypes) 43} 44 45// NewRTFiletype creates a new RTFiletype 46func NewRTFiletype() int { 47 NumTypes++ 48 allFiles = append(allFiles, []RuntimeFile{}) 49 realFiles = append(realFiles, []RuntimeFile{}) 50 return NumTypes - 1 51} 52 53// some file on filesystem 54type realFile string 55 56// some asset file 57type assetFile string 58 59// some file on filesystem but with a different name 60type namedFile struct { 61 realFile 62 name string 63} 64 65// a file with the data stored in memory 66type memoryFile struct { 67 name string 68 data []byte 69} 70 71func (mf memoryFile) Name() string { 72 return mf.name 73} 74func (mf memoryFile) Data() ([]byte, error) { 75 return mf.data, nil 76} 77 78func (rf realFile) Name() string { 79 fn := filepath.Base(string(rf)) 80 return fn[:len(fn)-len(filepath.Ext(fn))] 81} 82 83func (rf realFile) Data() ([]byte, error) { 84 return ioutil.ReadFile(string(rf)) 85} 86 87func (af assetFile) Name() string { 88 fn := path.Base(string(af)) 89 return fn[:len(fn)-len(path.Ext(fn))] 90} 91 92func (af assetFile) Data() ([]byte, error) { 93 return Asset(string(af)) 94} 95 96func (nf namedFile) Name() string { 97 return nf.name 98} 99 100// AddRuntimeFile registers a file for the given filetype 101func AddRuntimeFile(fileType RTFiletype, file RuntimeFile) { 102 allFiles[fileType] = append(allFiles[fileType], file) 103} 104 105// AddRealRuntimeFile registers a file for the given filetype 106func AddRealRuntimeFile(fileType RTFiletype, file RuntimeFile) { 107 allFiles[fileType] = append(allFiles[fileType], file) 108 realFiles[fileType] = append(realFiles[fileType], file) 109} 110 111// AddRuntimeFilesFromDirectory registers each file from the given directory for 112// the filetype which matches the file-pattern 113func AddRuntimeFilesFromDirectory(fileType RTFiletype, directory, pattern string) { 114 files, _ := ioutil.ReadDir(directory) 115 for _, f := range files { 116 if ok, _ := filepath.Match(pattern, f.Name()); !f.IsDir() && ok { 117 fullPath := filepath.Join(directory, f.Name()) 118 AddRealRuntimeFile(fileType, realFile(fullPath)) 119 } 120 } 121} 122 123// AddRuntimeFilesFromAssets registers each file from the given asset-directory for 124// the filetype which matches the file-pattern 125func AddRuntimeFilesFromAssets(fileType RTFiletype, directory, pattern string) { 126 files, err := AssetDir(directory) 127 if err != nil { 128 return 129 } 130 for _, f := range files { 131 if ok, _ := path.Match(pattern, f); ok { 132 AddRuntimeFile(fileType, assetFile(path.Join(directory, f))) 133 } 134 } 135} 136 137// FindRuntimeFile finds a runtime file of the given filetype and name 138// will return nil if no file was found 139func FindRuntimeFile(fileType RTFiletype, name string) RuntimeFile { 140 for _, f := range ListRuntimeFiles(fileType) { 141 if f.Name() == name { 142 return f 143 } 144 } 145 return nil 146} 147 148// ListRuntimeFiles lists all known runtime files for the given filetype 149func ListRuntimeFiles(fileType RTFiletype) []RuntimeFile { 150 return allFiles[fileType] 151} 152 153// ListRealRuntimeFiles lists all real runtime files (on disk) for a filetype 154// these runtime files will be ones defined by the user and loaded from the config directory 155func ListRealRuntimeFiles(fileType RTFiletype) []RuntimeFile { 156 return realFiles[fileType] 157} 158 159// InitRuntimeFiles initializes all assets file and the config directory 160func InitRuntimeFiles() { 161 add := func(fileType RTFiletype, dir, pattern string) { 162 AddRuntimeFilesFromDirectory(fileType, filepath.Join(ConfigDir, dir), pattern) 163 AddRuntimeFilesFromAssets(fileType, path.Join("runtime", dir), pattern) 164 } 165 166 add(RTColorscheme, "colorschemes", "*.micro") 167 add(RTSyntax, "syntax", "*.yaml") 168 add(RTSyntaxHeader, "syntax", "*.hdr") 169 add(RTHelp, "help", "*.md") 170 171 initlua := filepath.Join(ConfigDir, "init.lua") 172 if _, err := os.Stat(initlua); !os.IsNotExist(err) { 173 p := new(Plugin) 174 p.Name = "initlua" 175 p.DirName = "initlua" 176 p.Srcs = append(p.Srcs, realFile(initlua)) 177 Plugins = append(Plugins, p) 178 } 179 180 // Search ConfigDir for plugin-scripts 181 plugdir := filepath.Join(ConfigDir, "plug") 182 files, _ := ioutil.ReadDir(plugdir) 183 184 isID := regexp.MustCompile(`^[_A-Za-z0-9]+$`).MatchString 185 186 for _, d := range files { 187 if d.IsDir() { 188 srcs, _ := ioutil.ReadDir(filepath.Join(plugdir, d.Name())) 189 p := new(Plugin) 190 p.Name = d.Name() 191 p.DirName = d.Name() 192 for _, f := range srcs { 193 if strings.HasSuffix(f.Name(), ".lua") { 194 p.Srcs = append(p.Srcs, realFile(filepath.Join(plugdir, d.Name(), f.Name()))) 195 } else if strings.HasSuffix(f.Name(), ".json") { 196 data, err := ioutil.ReadFile(filepath.Join(plugdir, d.Name(), f.Name())) 197 if err != nil { 198 continue 199 } 200 p.Info, err = NewPluginInfo(data) 201 if err != nil { 202 continue 203 } 204 p.Name = p.Info.Name 205 } 206 } 207 208 if !isID(p.Name) || len(p.Srcs) <= 0 { 209 log.Println(p.Name, "is not a plugin") 210 continue 211 } 212 Plugins = append(Plugins, p) 213 } 214 } 215 216 plugdir = filepath.Join("runtime", "plugins") 217 if files, err := AssetDir(plugdir); err == nil { 218 for _, d := range files { 219 if srcs, err := AssetDir(filepath.Join(plugdir, d)); err == nil { 220 p := new(Plugin) 221 p.Name = d 222 p.DirName = d 223 p.Default = true 224 for _, f := range srcs { 225 if strings.HasSuffix(f, ".lua") { 226 p.Srcs = append(p.Srcs, assetFile(filepath.Join(plugdir, d, f))) 227 } else if strings.HasSuffix(f, ".json") { 228 data, err := Asset(filepath.Join(plugdir, d, f)) 229 if err != nil { 230 continue 231 } 232 p.Info, err = NewPluginInfo(data) 233 if err != nil { 234 continue 235 } 236 p.Name = p.Info.Name 237 } 238 } 239 if !isID(p.Name) || len(p.Srcs) <= 0 { 240 log.Println(p.Name, "is not a plugin") 241 continue 242 } 243 Plugins = append(Plugins, p) 244 } 245 } 246 } 247} 248 249// PluginReadRuntimeFile allows plugin scripts to read the content of a runtime file 250func PluginReadRuntimeFile(fileType RTFiletype, name string) string { 251 if file := FindRuntimeFile(fileType, name); file != nil { 252 if data, err := file.Data(); err == nil { 253 return string(data) 254 } 255 } 256 return "" 257} 258 259// PluginListRuntimeFiles allows plugins to lists all runtime files of the given type 260func PluginListRuntimeFiles(fileType RTFiletype) []string { 261 files := ListRuntimeFiles(fileType) 262 result := make([]string, len(files)) 263 for i, f := range files { 264 result[i] = f.Name() 265 } 266 return result 267} 268 269// PluginAddRuntimeFile adds a file to the runtime files for a plugin 270func PluginAddRuntimeFile(plugin string, filetype RTFiletype, filePath string) error { 271 pl := FindPlugin(plugin) 272 if pl == nil { 273 return errors.New("Plugin " + plugin + " does not exist") 274 } 275 pldir := pl.DirName 276 fullpath := filepath.Join(ConfigDir, "plug", pldir, filePath) 277 if _, err := os.Stat(fullpath); err == nil { 278 AddRealRuntimeFile(filetype, realFile(fullpath)) 279 } else { 280 fullpath = path.Join("runtime", "plugins", pldir, filePath) 281 AddRuntimeFile(filetype, assetFile(fullpath)) 282 } 283 return nil 284} 285 286// PluginAddRuntimeFilesFromDirectory adds files from a directory to the runtime files for a plugin 287func PluginAddRuntimeFilesFromDirectory(plugin string, filetype RTFiletype, directory, pattern string) error { 288 pl := FindPlugin(plugin) 289 if pl == nil { 290 return errors.New("Plugin " + plugin + " does not exist") 291 } 292 pldir := pl.DirName 293 fullpath := filepath.Join(ConfigDir, "plug", pldir, directory) 294 if _, err := os.Stat(fullpath); err == nil { 295 AddRuntimeFilesFromDirectory(filetype, fullpath, pattern) 296 } else { 297 fullpath = path.Join("runtime", "plugins", pldir, directory) 298 AddRuntimeFilesFromAssets(filetype, fullpath, pattern) 299 } 300 return nil 301} 302 303// PluginAddRuntimeFileFromMemory adds a file to the runtime files for a plugin from a given string 304func PluginAddRuntimeFileFromMemory(filetype RTFiletype, filename, data string) { 305 AddRealRuntimeFile(filetype, memoryFile{filename, []byte(data)}) 306} 307