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