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