Merge pull request #363 from boombuler/rtfiles

Runtime files
This commit is contained in:
Zachary Yedidia
2016-09-16 10:33:28 -04:00
committed by GitHub
9 changed files with 194 additions and 230 deletions

View File

@@ -84,7 +84,8 @@ func CommandComplete(input string) (string, []string) {
func HelpComplete(input string) (string, []string) {
var suggestions []string
for topic, _ := range helpPages {
for _, file := range ListRuntimeFiles(FILE_Help) {
topic := file.Name()
if strings.HasPrefix(topic, input) {
suggestions = append(suggestions, topic)
}

View File

@@ -2,7 +2,6 @@ package main
import (
"fmt"
"io/ioutil"
"regexp"
"strconv"
"strings"
@@ -16,66 +15,53 @@ type Colorscheme map[string]tcell.Style
// The current colorscheme
var colorscheme Colorscheme
var preInstalledColors = []string{"default", "simple", "solarized", "solarized-tc", "atom-dark-tc", "monokai", "gruvbox", "zenburn", "bubblegum"}
// ColorschemeExists checks if a given colorscheme exists
func ColorschemeExists(colorschemeName string) bool {
files, _ := ioutil.ReadDir(configDir + "/colorschemes")
for _, f := range files {
if f.Name() == colorschemeName+".micro" {
return true
}
}
for _, name := range preInstalledColors {
if name == colorschemeName {
return true
}
}
return false
return FindRuntimeFile(FILE_ColorScheme, colorschemeName) != nil
}
// InitColorscheme picks and initializes the colorscheme when micro starts
func InitColorscheme() {
colorscheme = make(Colorscheme)
if screen != nil {
screen.SetStyle(tcell.StyleDefault.
Foreground(tcell.ColorDefault).
Background(tcell.ColorDefault))
}
LoadDefaultColorscheme()
}
// LoadDefaultColorscheme loads the default colorscheme from $(configDir)/colorschemes
func LoadDefaultColorscheme() {
LoadColorscheme(globalSettings["colorscheme"].(string), configDir+"/colorschemes")
LoadColorscheme(globalSettings["colorscheme"].(string))
}
// LoadColorscheme loads the given colorscheme from a directory
func LoadColorscheme(colorschemeName, dir string) {
files, _ := ioutil.ReadDir(dir)
found := false
for _, f := range files {
if f.Name() == colorschemeName+".micro" {
text, err := ioutil.ReadFile(dir + "/" + f.Name())
if err != nil {
fmt.Println("Error loading colorscheme:", err)
continue
}
colorscheme = ParseColorscheme(string(text))
found = true
}
}
for _, name := range preInstalledColors {
if name == colorschemeName {
data, err := Asset("runtime/colorschemes/" + name + ".micro")
if err != nil {
TermMessage("Unable to load pre-installed colorscheme " + name)
continue
}
colorscheme = ParseColorscheme(string(data))
found = true
}
}
if !found {
func LoadColorscheme(colorschemeName string) {
file := FindRuntimeFile(FILE_ColorScheme, colorschemeName)
if file == nil {
TermMessage(colorschemeName, "is not a valid colorscheme")
} else {
if data, err := file.Data(); err != nil {
fmt.Println("Error loading colorscheme:", err)
} else {
colorscheme = ParseColorscheme(string(data))
// Default style
defStyle = tcell.StyleDefault.
Foreground(tcell.ColorDefault).
Background(tcell.ColorDefault)
// There may be another default style defined in the colorscheme
// In that case we should use that one
if style, ok := colorscheme["default"]; ok {
defStyle = style
}
if screen != nil {
screen.SetStyle(defStyle)
}
}
}
}

View File

@@ -94,7 +94,7 @@ func Help(args []string) {
CurView().openHelp("help")
} else {
helpPage := args[0]
if _, ok := helpPages[helpPage]; ok {
if FindRuntimeFile(FILE_Help, helpPage) != nil {
CurView().openHelp(helpPage)
} else {
messenger.Error("Sorry, no help for ", helpPage)

View File

@@ -1,38 +0,0 @@
package main
import (
"io/ioutil"
)
type HelpPage interface {
HelpFile() ([]byte, error)
}
var helpPages map[string]HelpPage = map[string]HelpPage{
"help": assetHelpPage("help"),
"keybindings": assetHelpPage("keybindings"),
"plugins": assetHelpPage("plugins"),
"colors": assetHelpPage("colors"),
"options": assetHelpPage("options"),
"commands": assetHelpPage("commands"),
"tutorial": assetHelpPage("tutorial"),
}
type assetHelpPage string
func (file assetHelpPage) HelpFile() ([]byte, error) {
return Asset("runtime/help/" + string(file) + ".md")
}
type fileHelpPage string
func (file fileHelpPage) HelpFile() ([]byte, error) {
return ioutil.ReadFile(string(file))
}
func AddPluginHelp(name, file string) {
if _, exists := helpPages[name]; exists {
return
}
helpPages[name] = fileHelpPage(file)
}

View File

@@ -1,8 +1,6 @@
package main
import (
"io/ioutil"
"path/filepath"
"regexp"
"strings"
@@ -30,150 +28,16 @@ type SyntaxRule struct {
var syntaxFiles map[[2]*regexp.Regexp]FileTypeRules
// These syntax files are pre installed and embedded in the resulting binary by go-bindata
var preInstalledSynFiles = []string{
"Dockerfile",
"apacheconf",
"arduino",
"asciidoc",
"asm",
"awk",
"c",
"caddyfile",
"cmake",
"coffeescript",
"colortest",
"conf",
"conky",
"csharp",
"css",
"cython",
"d",
"dart",
"dot",
"erb",
"fish",
"fortran",
"gdscript",
"gentoo-ebuild",
"gentoo-etc-portage",
"git-commit",
"git-config",
"git-rebase-todo",
"glsl",
"go",
"golo",
"groff",
"haml",
"haskell",
"html",
"ini",
"inputrc",
"java",
"javascript",
"json",
"keymap",
"kickstart",
"ledger",
"lilypond",
"lisp",
"lua",
"makefile",
"man",
"markdown",
"mpdconf",
"micro",
"nanorc",
"nginx",
"ocaml",
"pascal",
"patch",
"peg",
"perl",
"perl6",
"php",
"pkg-config",
"po",
"pov",
"privoxy-action",
"privoxy-config",
"privoxy-filter",
"puppet",
"python",
"r",
"reST",
"rpmspec",
"ruby",
"rust",
"scala",
"sed",
"sh",
"sls",
"sql",
"swift",
"systemd",
"tcl",
"tex",
"vala",
"vi",
"xml",
"xresources",
"yaml",
"yum",
"zsh",
}
// LoadSyntaxFiles loads the syntax files from the default directory (configDir)
func LoadSyntaxFiles() {
// Load the user's custom syntax files, if there are any
LoadSyntaxFilesFromDir(configDir + "/syntax")
// Load the pre-installed syntax files from inside the binary
for _, filetype := range preInstalledSynFiles {
data, err := Asset("runtime/syntax/" + filetype + ".micro")
if err != nil {
TermMessage("Unable to load pre-installed syntax file " + filetype)
continue
}
LoadSyntaxFile(string(data), filetype+".micro")
}
}
// LoadSyntaxFilesFromDir loads the syntax files from a specified directory
// To load the syntax files, we must fill the `syntaxFiles` map
// This involves finding the regex for syntax and if it exists, the regex
// for the header. Then we must get the text for the file and the filetype.
func LoadSyntaxFilesFromDir(dir string) {
colorscheme = make(Colorscheme)
InitColorscheme()
// Default style
defStyle = tcell.StyleDefault.
Foreground(tcell.ColorDefault).
Background(tcell.ColorDefault)
// There may be another default style defined in the colorscheme
// In that case we should use that one
if style, ok := colorscheme["default"]; ok {
defStyle = style
}
if screen != nil {
screen.SetStyle(defStyle)
}
syntaxFiles = make(map[[2]*regexp.Regexp]FileTypeRules)
files, _ := ioutil.ReadDir(dir)
for _, f := range files {
if filepath.Ext(f.Name()) == ".micro" {
filename := dir + "/" + f.Name()
text, err := ioutil.ReadFile(filename)
if err != nil {
TermMessage("Error loading syntax file " + filename + ": " + err.Error())
return
}
LoadSyntaxFile(string(text), filename)
for _, f := range ListRuntimeFiles(FILE_Syntax) {
data, err := f.Data()
if err != nil {
TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error())
} else {
LoadSyntaxFile(string(data), f.Name())
}
}
}

View File

@@ -233,6 +233,9 @@ func main() {
// Find the user's configuration directory (probably $XDG_CONFIG_HOME/micro)
InitConfigDir()
// Build a list of available Extensions (Syntax, Colorscheme etc.)
InitRuntimeFiles()
// Load the user's settings
InitGlobalSettings()
@@ -313,6 +316,10 @@ func main() {
L.SetGlobal("JobSend", luar.New(L, JobSend))
L.SetGlobal("JobStop", luar.New(L, JobStop))
// Extension Files
L.SetGlobal("ReadRuntimeFile", luar.New(L, PluginReadRuntimeFile))
L.SetGlobal("ListRuntimeFiles", luar.New(L, PluginListRuntimeFiles))
LoadPlugins()
jobs = make(chan JobFunction, 100)

View File

@@ -138,7 +138,7 @@ func LoadPlugins() {
}
loadedPlugins = append(loadedPlugins, pluginName)
} else if f.Name() == "help.md" {
AddPluginHelp(pluginName, fullPath)
AddRuntimeFile(FILE_Help, namedFile{realFile(fullPath), pluginName})
}
}
}

144
cmd/micro/rtfiles.go Normal file
View File

@@ -0,0 +1,144 @@
package main
import (
"io/ioutil"
"path"
"path/filepath"
)
const (
FILE_ColorScheme = "colorscheme"
FILE_Syntax = "syntax"
FILE_Help = "help"
)
// 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 map[string][]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
}
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 string, file RuntimeFile) {
if allFiles == nil {
allFiles = make(map[string][]RuntimeFile)
}
allFiles[fileType] = append(allFiles[fileType], file)
}
// AddRuntimeFilesFromDirectory registers each file from the given directory for
// the filetype which matches the file-pattern
func AddRuntimeFilesFromDirectory(fileType, 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())
AddRuntimeFile(fileType, realFile(fullPath))
}
}
}
// AddRuntimeFilesFromDirectory registers each file from the given asset-directory for
// the filetype which matches the file-pattern
func AddRuntimeFilesFromAssets(fileType, 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, name string) RuntimeFile {
for _, f := range ListRuntimeFiles(fileType) {
if f.Name() == name {
return f
}
}
return nil
}
// Lists all known runtime files for the given filetype
func ListRuntimeFiles(fileType string) []RuntimeFile {
if files, ok := allFiles[fileType]; ok {
return files
} else {
return []RuntimeFile{}
}
}
// Initializes all assets file and the config directory
func InitRuntimeFiles() {
add := func(fileType, dir, pattern string) {
AddRuntimeFilesFromDirectory(fileType, filepath.Join(configDir, dir), pattern)
AddRuntimeFilesFromAssets(fileType, path.Join("runtime", dir), pattern)
}
add(FILE_ColorScheme, "colorschemes", "*.micro")
add(FILE_Syntax, "syntax", "*.micro")
add(FILE_Help, "help", "*.md")
}
// Allows plugin scripts to read the content of a runtime file
func PluginReadRuntimeFile(fileType, name string) string {
if file := FindRuntimeFile(fileType, name); file != nil {
if data, err := file.Data(); err == nil {
return string(data)
}
}
return ""
}
// Allows plugins to lists all runtime files of the given type
func PluginListRuntimeFiles(fileType string) []string {
files := ListRuntimeFiles(fileType)
result := make([]string, len(files))
for i, f := range files {
result[i] = f.Name()
}
return result
}

View File

@@ -529,7 +529,7 @@ func (v *View) ClearAllGutterMessages() {
// Opens the given help page in a new horizontal split
func (v *View) openHelp(helpPage string) {
if data, err := helpPages[helpPage].HelpFile(); err != nil {
if data, err := FindRuntimeFile(FILE_Help, helpPage).Data(); err != nil {
TermMessage("Unable to load help text", helpPage, "\n", err)
} else {
helpBuffer := NewBuffer(data, helpPage+".md")