]> git.lizzy.rs Git - micro.git/blob - internal/config/rtfiles.go
09e9176883cbabc712d3ce3f5dca33cf18aaa4ff
[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 // NewRTFiletype creates a new RTFiletype
46 func 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
54 type realFile string
55
56 // some asset file
57 type assetFile string
58
59 // some file on filesystem but with a different name
60 type namedFile struct {
61         realFile
62         name string
63 }
64
65 // a file with the data stored in memory
66 type memoryFile struct {
67         name string
68         data []byte
69 }
70
71 func (mf memoryFile) Name() string {
72         return mf.name
73 }
74 func (mf memoryFile) Data() ([]byte, error) {
75         return mf.data, nil
76 }
77
78 func (rf realFile) Name() string {
79         fn := filepath.Base(string(rf))
80         return fn[:len(fn)-len(filepath.Ext(fn))]
81 }
82
83 func (rf realFile) Data() ([]byte, error) {
84         return ioutil.ReadFile(string(rf))
85 }
86
87 func (af assetFile) Name() string {
88         fn := path.Base(string(af))
89         return fn[:len(fn)-len(path.Ext(fn))]
90 }
91
92 func (af assetFile) Data() ([]byte, error) {
93         return Asset(string(af))
94 }
95
96 func (nf namedFile) Name() string {
97         return nf.name
98 }
99
100 // AddRuntimeFile registers a file for the given filetype
101 func AddRuntimeFile(fileType RTFiletype, file RuntimeFile) {
102         allFiles[fileType] = append(allFiles[fileType], file)
103 }
104
105 // AddRealRuntimeFile registers a file for the given filetype
106 func 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
113 func 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
125 func 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
139 func 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
149 func 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
155 func ListRealRuntimeFiles(fileType RTFiletype) []RuntimeFile {
156         return realFiles[fileType]
157 }
158
159 // InitRuntimeFiles initializes all assets file and the config directory
160 func 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                                                 log.Println(err)
203                                                 continue
204                                         }
205                                         p.Name = p.Info.Name
206                                 }
207                         }
208
209                         if !isID(p.Name) || len(p.Srcs) <= 0 {
210                                 log.Println(p.Name, "is not a plugin")
211                                 continue
212                         }
213                         Plugins = append(Plugins, p)
214                 }
215         }
216
217         plugdir = filepath.Join("runtime", "plugins")
218         if files, err := AssetDir(plugdir); err == nil {
219                 for _, d := range files {
220                         if srcs, err := AssetDir(filepath.Join(plugdir, d)); err == nil {
221                                 p := new(Plugin)
222                                 p.Name = d
223                                 p.DirName = d
224                                 p.Default = true
225                                 for _, f := range srcs {
226                                         if strings.HasSuffix(f, ".lua") {
227                                                 p.Srcs = append(p.Srcs, assetFile(filepath.Join(plugdir, d, f)))
228                                         } else if strings.HasSuffix(f, ".json") {
229                                                 data, err := Asset(filepath.Join(plugdir, d, f))
230                                                 if err != nil {
231                                                         continue
232                                                 }
233                                                 p.Info, err = NewPluginInfo(data)
234                                                 if err != nil {
235                                                         log.Println(err)
236                                                         continue
237                                                 }
238                                                 p.Name = p.Info.Name
239                                         }
240                                 }
241                                 if !isID(p.Name) || len(p.Srcs) <= 0 {
242                                         log.Println(p.Name, "is not a plugin")
243                                         continue
244                                 }
245                                 Plugins = append(Plugins, p)
246                         }
247                 }
248         }
249 }
250
251 // PluginReadRuntimeFile allows plugin scripts to read the content of a runtime file
252 func PluginReadRuntimeFile(fileType RTFiletype, name string) string {
253         if file := FindRuntimeFile(fileType, name); file != nil {
254                 if data, err := file.Data(); err == nil {
255                         return string(data)
256                 }
257         }
258         return ""
259 }
260
261 // PluginListRuntimeFiles allows plugins to lists all runtime files of the given type
262 func PluginListRuntimeFiles(fileType RTFiletype) []string {
263         files := ListRuntimeFiles(fileType)
264         result := make([]string, len(files))
265         for i, f := range files {
266                 result[i] = f.Name()
267         }
268         return result
269 }
270
271 // PluginAddRuntimeFile adds a file to the runtime files for a plugin
272 func PluginAddRuntimeFile(plugin string, filetype RTFiletype, filePath string) error {
273         pl := FindPlugin(plugin)
274         if pl == nil {
275                 return errors.New("Plugin " + plugin + " does not exist")
276         }
277         pldir := pl.DirName
278         fullpath := filepath.Join(ConfigDir, "plug", pldir, filePath)
279         if _, err := os.Stat(fullpath); err == nil {
280                 AddRealRuntimeFile(filetype, realFile(fullpath))
281         } else {
282                 fullpath = path.Join("runtime", "plugins", pldir, filePath)
283                 AddRuntimeFile(filetype, assetFile(fullpath))
284         }
285         return nil
286 }
287
288 // PluginAddRuntimeFilesFromDirectory adds files from a directory to the runtime files for a plugin
289 func PluginAddRuntimeFilesFromDirectory(plugin string, filetype RTFiletype, directory, pattern string) error {
290         pl := FindPlugin(plugin)
291         if pl == nil {
292                 return errors.New("Plugin " + plugin + " does not exist")
293         }
294         pldir := pl.DirName
295         fullpath := filepath.Join(ConfigDir, "plug", pldir, directory)
296         if _, err := os.Stat(fullpath); err == nil {
297                 AddRuntimeFilesFromDirectory(filetype, fullpath, pattern)
298         } else {
299                 fullpath = path.Join("runtime", "plugins", pldir, directory)
300                 AddRuntimeFilesFromAssets(filetype, fullpath, pattern)
301         }
302         return nil
303 }
304
305 // PluginAddRuntimeFileFromMemory adds a file to the runtime files for a plugin from a given string
306 func PluginAddRuntimeFileFromMemory(filetype RTFiletype, filename, data string) {
307         AddRealRuntimeFile(filetype, memoryFile{filename, []byte(data)})
308 }