This commit is contained in:
Zachary Yedidia 2016-09-18 09:30:28 -04:00
commit d41f0bb324
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) {
var suggestions []string
for topic, _ := range helpPages {
for _, file := range ListRuntimeFiles(RTHelp) {
topic := file.Name()
if strings.HasPrefix(topic, input) {
suggestions = append(suggestions, topic)
}

View file

@ -405,7 +405,6 @@ func DefaultBindings() map[string]string {
"CtrlR": "ToggleRuler",
"CtrlL": "JumpLine",
"Delete": "Delete",
"Esc": "ClearStatus",
"CtrlB": "ShellMode",
"CtrlQ": "Quit",
"CtrlE": "CommandMode",
@ -420,5 +419,13 @@ func DefaultBindings() map[string]string {
"Alt-e": "EndOfLine",
"Alt-p": "CursorUp",
"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 (
"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(RTColorscheme, 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(RTColorscheme, 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(RTHelp, 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(RTSyntax) {
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,11 @@ 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))
L.SetGlobal("AddRuntimeFile", luar.New(L, PluginAddRuntimeFile))
LoadPlugins()
jobs = make(chan JobFunction, 100)

View file

@ -137,8 +137,6 @@ func LoadPlugins() {
continue
}
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
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)
} else {
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,
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`.
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
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-e": "EndOfLine",
"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
`~/.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
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
@ -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)`
# 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
See this example to learn how to use `MakeCompletion` and `MakeCommand`

View file

@ -1,5 +1,5 @@
syntax "yaml" "\.ya?ml$"
header "^---" "%YAML"
header "%YAML"
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"