This commit is contained in:
Zachary Yedidia
2016-09-18 09:30:28 -04:00
15 changed files with 264 additions and 242 deletions

View File

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

View File

@@ -405,7 +405,6 @@ func DefaultBindings() map[string]string {
"CtrlR": "ToggleRuler", "CtrlR": "ToggleRuler",
"CtrlL": "JumpLine", "CtrlL": "JumpLine",
"Delete": "Delete", "Delete": "Delete",
"Esc": "ClearStatus",
"CtrlB": "ShellMode", "CtrlB": "ShellMode",
"CtrlQ": "Quit", "CtrlQ": "Quit",
"CtrlE": "CommandMode", "CtrlE": "CommandMode",
@@ -420,5 +419,13 @@ func DefaultBindings() map[string]string {
"Alt-e": "EndOfLine", "Alt-e": "EndOfLine",
"Alt-p": "CursorUp", "Alt-p": "CursorUp",
"Alt-n": "CursorDown", "Alt-n": "CursorDown",
// Integration with file managers
"F1": "ToggleHelp",
"F2": "Save",
"F4": "Quit",
"F7": "Find",
"F10": "Quit",
"Esc": "Quit",
} }
} }

View File

@@ -2,7 +2,6 @@ package main
import ( import (
"fmt" "fmt"
"io/ioutil"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@@ -16,66 +15,53 @@ type Colorscheme map[string]tcell.Style
// The current colorscheme // The current colorscheme
var colorscheme 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 // ColorschemeExists checks if a given colorscheme exists
func ColorschemeExists(colorschemeName string) bool { func ColorschemeExists(colorschemeName string) bool {
files, _ := ioutil.ReadDir(configDir + "/colorschemes") return FindRuntimeFile(RTColorscheme, colorschemeName) != nil
for _, f := range files {
if f.Name() == colorschemeName+".micro" {
return true
}
}
for _, name := range preInstalledColors {
if name == colorschemeName {
return true
}
}
return false
} }
// InitColorscheme picks and initializes the colorscheme when micro starts // InitColorscheme picks and initializes the colorscheme when micro starts
func InitColorscheme() { func InitColorscheme() {
colorscheme = make(Colorscheme)
if screen != nil {
screen.SetStyle(tcell.StyleDefault.
Foreground(tcell.ColorDefault).
Background(tcell.ColorDefault))
}
LoadDefaultColorscheme() LoadDefaultColorscheme()
} }
// LoadDefaultColorscheme loads the default colorscheme from $(configDir)/colorschemes // LoadDefaultColorscheme loads the default colorscheme from $(configDir)/colorschemes
func LoadDefaultColorscheme() { func LoadDefaultColorscheme() {
LoadColorscheme(globalSettings["colorscheme"].(string), configDir+"/colorschemes") LoadColorscheme(globalSettings["colorscheme"].(string))
} }
// LoadColorscheme loads the given colorscheme from a directory // LoadColorscheme loads the given colorscheme from a directory
func LoadColorscheme(colorschemeName, dir string) { func LoadColorscheme(colorschemeName string) {
files, _ := ioutil.ReadDir(dir) file := FindRuntimeFile(RTColorscheme, colorschemeName)
found := false if file == nil {
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 {
TermMessage(colorschemeName, "is not a valid colorscheme") 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") CurView().openHelp("help")
} else { } else {
helpPage := args[0] helpPage := args[0]
if _, ok := helpPages[helpPage]; ok { if FindRuntimeFile(RTHelp, helpPage) != nil {
CurView().openHelp(helpPage) CurView().openHelp(helpPage)
} else { } else {
messenger.Error("Sorry, no help for ", helpPage) 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 package main
import ( import (
"io/ioutil"
"path/filepath"
"regexp" "regexp"
"strings" "strings"
@@ -30,150 +28,16 @@ type SyntaxRule struct {
var syntaxFiles map[[2]*regexp.Regexp]FileTypeRules 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) // LoadSyntaxFiles loads the syntax files from the default directory (configDir)
func LoadSyntaxFiles() { 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() 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) syntaxFiles = make(map[[2]*regexp.Regexp]FileTypeRules)
files, _ := ioutil.ReadDir(dir) for _, f := range ListRuntimeFiles(RTSyntax) {
for _, f := range files { data, err := f.Data()
if filepath.Ext(f.Name()) == ".micro" { if err != nil {
filename := dir + "/" + f.Name() TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error())
text, err := ioutil.ReadFile(filename) } else {
LoadSyntaxFile(string(data), f.Name())
if err != nil {
TermMessage("Error loading syntax file " + filename + ": " + err.Error())
return
}
LoadSyntaxFile(string(text), filename)
} }
} }
} }

View File

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

View File

@@ -137,8 +137,6 @@ func LoadPlugins() {
continue continue
} }
loadedPlugins = append(loadedPlugins, pluginName) loadedPlugins = append(loadedPlugins, pluginName)
} else if f.Name() == "help.md" {
AddPluginHelp(pluginName, fullPath)
} }
} }
} }

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

@@ -0,0 +1,166 @@
package main
import (
"io/ioutil"
"os"
"path"
"path/filepath"
)
const (
RTColorscheme = "colorscheme"
RTSyntax = "syntax"
RTHelp = "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))
}
}
}
// AddRuntimeFilesFromAssets 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
}
// ListRuntimeFiles lists all known runtime files for the given filetype
func ListRuntimeFiles(fileType string) []RuntimeFile {
if files, ok := allFiles[fileType]; ok {
return files
}
return []RuntimeFile{}
}
// InitRuntimeFiles 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(RTColorscheme, "colorschemes", "*.micro")
add(RTSyntax, "syntax", "*.micro")
add(RTHelp, "help", "*.md")
}
// PluginReadRuntimeFile 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 ""
}
// PluginListRuntimeFiles 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
}
// PluginAddRuntimeFile adds a file to the runtime files for a plugin
func PluginAddRuntimeFile(plugin, filetype, path string) {
fullpath := configDir + "/plugins/" + plugin + "/" + path
if _, err := os.Stat(fullpath); err == nil {
AddRuntimeFile(filetype, realFile(fullpath))
} else {
fullpath = "runtime/plugins/" + plugin + "/" + path
AddRuntimeFile(filetype, assetFile(fullpath))
}
}
// PluginAddRuntimeFilesFromDirectory adds files from a directory to the runtime files for a plugin
func PluginAddRuntimeFilesFromDirectory(plugin, filetype, directory, pattern string) {
fullpath := filepath.Join(configDir, "plugins", plugin, directory)
if _, err := os.Stat(fullpath); err == nil {
AddRuntimeFilesFromDirectory(filetype, fullpath, pattern)
} else {
fullpath = path.Join("runtime", "plugins", plugin, directory)
AddRuntimeFilesFromAssets(filetype, fullpath, pattern)
}
}

File diff suppressed because one or more lines are too long

View File

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

View File

@@ -3,12 +3,26 @@
Micro is a terminal-based text editor that aims to be easy to use and intuitive, Micro is a terminal-based text editor that aims to be easy to use and intuitive,
while also taking advantage of the full capabilities of modern terminals. while also taking advantage of the full capabilities of modern terminals.
*Press CtrlQ to quit, and CtrlS to save.*
If you want to see all the keybindings press CtrlE and type `help keybindings`. If you want to see all the keybindings press CtrlE and type `help keybindings`.
See the next section for more information about documentation and help. See the next section for more information about documentation and help.
### Quick-start
Press CtrlQ to quit, and CtrlS to save. Press CtrlE to start typing commands
and you can see which commands are available by pressing tab, or by
viewing the help topic `> help commands`. When I write `> ...` I mean press
CtrlE and then type whatever is there.
Move the cursor around with the mouse or the arrow keys.
If the colorscheme doesn't look good, you can change it with `> set colorscheme ...`.
You can press tab to see the available colorschemes, or see more information with
`> help colors`.
Press CtrlW to move between splits, and type `> vsplit filename` or `> hsplit filename`
to open a new split.
### Accessing more help ### Accessing more help
Micro has a built-in help system much like Vim's (although less extensive). Micro has a built-in help system much like Vim's (although less extensive).

View File

@@ -72,7 +72,15 @@ you can rebind them to your liking.
"Alt-a": "StartOfLine", "Alt-a": "StartOfLine",
"Alt-e": "EndOfLine", "Alt-e": "EndOfLine",
"Alt-p": "CursorUp", "Alt-p": "CursorUp",
"Alt-n": "CursorDown" "Alt-n": "CursorDown",
// Integration with file managers
"F1": "ToggleHelp",
"F2": "Save",
"F4": "Quit",
"F7": "Find",
"F10": "Quit",
"Esc": "Quit",
} }
``` ```

View File

@@ -4,8 +4,6 @@ Micro supports creating plugins with a simple Lua system. Every plugin has a
main script which is run at startup which should be placed in main script which is run at startup which should be placed in
`~/.config/micro/plugins/pluginName/pluginName.lua`. `~/.config/micro/plugins/pluginName/pluginName.lua`.
If you want to add a help page for your plugin, place a markdown file in `~/.config/micro/plugins/pluginName/help.md`.
There are a number of callback functions which you can create in your There are a number of callback functions which you can create in your
plugin to run code at times other than startup. The naming scheme is plugin to run code at times other than startup. The naming scheme is
`onAction(view)`. For example a function which is run every time the user saves `onAction(view)`. For example a function which is run every time the user saves
@@ -111,6 +109,16 @@ The possible methods which you can call using the `messenger` variable are:
If you want a standard prompt, just use `messenger.Prompt(prompt, "", 0)` If you want a standard prompt, just use `messenger.Prompt(prompt, "", 0)`
# Adding help files, syntax files, or colorschemes in your plugin
You can use the `AddRuntimeFile(name, type, path string)` function to add various kinds of
files to your plugin. For example, if you'd like to add a help topic and to your plugin
called `test`, you would create the `test.md` file for example, and runt the function:
```lua
AddRuntimeFile("test", "help", "test.md")
```
# Autocomplete command arguments # Autocomplete command arguments
See this example to learn how to use `MakeCompletion` and `MakeCommand` See this example to learn how to use `MakeCompletion` and `MakeCommand`

View File

@@ -1,5 +1,5 @@
syntax "yaml" "\.ya?ml$" syntax "yaml" "\.ya?ml$"
header "^---" "%YAML" header "%YAML"
color type "(^| )!!(binary|bool|float|int|map|null|omap|seq|set|str) " color type "(^| )!!(binary|bool|float|int|map|null|omap|seq|set|str) "
color constant "\b(YES|yes|Y|y|ON|on|NO|no|N|n|OFF|off)\b" color constant "\b(YES|yes|Y|y|ON|on|NO|no|N|n|OFF|off)\b"