mirror of
https://github.com/zyedidia/micro.git
synced 2026-04-01 15:47:12 +09:00
Compare commits
80 Commits
highlight-
...
insert-per
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca9d102267 | ||
|
|
c3d120ccdf | ||
|
|
a4a6445e1d | ||
|
|
13e30a63eb | ||
|
|
e561952781 | ||
|
|
9fe58bf84f | ||
|
|
e84f85340b | ||
|
|
aaf90c6f52 | ||
|
|
0962e1bfba | ||
|
|
2f45644d14 | ||
|
|
c2a2316c28 | ||
|
|
6957e83cdb | ||
|
|
ce91e41e5a | ||
|
|
6d99d34eb0 | ||
|
|
b77980082c | ||
|
|
2fd59adffa | ||
|
|
57c34e2248 | ||
|
|
6514b77e0d | ||
|
|
8a907956d1 | ||
|
|
c4bfa825a1 | ||
|
|
b0c50d371f | ||
|
|
b4d4119572 | ||
|
|
674258787e | ||
|
|
2354412922 | ||
|
|
3ac30343b8 | ||
|
|
37343350ca | ||
|
|
d9e9d4403f | ||
|
|
741f494841 | ||
|
|
69b6c724fc | ||
|
|
31936358c1 | ||
|
|
fe58ff5753 | ||
|
|
9aaafe5dcf | ||
|
|
c2bd5e4eec | ||
|
|
98ddb62af4 | ||
|
|
24a684cff2 | ||
|
|
b4e7e981f3 | ||
|
|
e73549c61a | ||
|
|
e759d4087b | ||
|
|
106ba48079 | ||
|
|
ef768e36f3 | ||
|
|
f5e1f93ee5 | ||
|
|
a52c0c2907 | ||
|
|
be7d27bc49 | ||
|
|
f6a9c482a6 | ||
|
|
6e3f38b271 | ||
|
|
8483f1da1e | ||
|
|
28ed47e358 | ||
|
|
6a1b8f4a4f | ||
|
|
dba8ef2fdd | ||
|
|
b0624cb66e | ||
|
|
09ea82c97e | ||
|
|
d94b81b8e6 | ||
|
|
bcb1947a0a | ||
|
|
b0b5d7b392 | ||
|
|
2598d8ad70 | ||
|
|
f731e422ea | ||
|
|
abcdeb01e9 | ||
|
|
d326a9cddd | ||
|
|
e3131a0779 | ||
|
|
46c5a81b0d | ||
|
|
59146cabb1 | ||
|
|
35e3bddea0 | ||
|
|
016b8dcc4c | ||
|
|
03228762d4 | ||
|
|
953f5a0eff | ||
|
|
063389afdf | ||
|
|
1e998ab0e4 | ||
|
|
0827968f6b | ||
|
|
89ac5d7de2 | ||
|
|
612ebb2e17 | ||
|
|
26172b5101 | ||
|
|
51691ed7bf | ||
|
|
5acbccf0b2 | ||
|
|
87661ef308 | ||
|
|
58e11c0b2d | ||
|
|
457a4f8f98 | ||
|
|
e3fd914e0b | ||
|
|
6aa5aa540b | ||
|
|
81bad4d089 | ||
|
|
9f4da789db |
@@ -1,4 +1,4 @@
|
||||
Third party libraries
|
||||
Third party libraries directly used by micro and their licenses
|
||||
================
|
||||
|
||||
github.com/golang/go/LICENSE
|
||||
@@ -637,37 +637,6 @@ github.com/zyedidia/tcell/LICENSE (fork)
|
||||
limitations under the License.
|
||||
|
||||
|
||||
golang.org/x/net/LICENSE
|
||||
================
|
||||
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
golang.org/x/text/LICENSE
|
||||
================
|
||||
|
||||
@@ -1079,6 +1048,8 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
|
||||
github.com/flynn/json5/LICENSE
|
||||
================
|
||||
github.com/zyedidia/json5/LICENSE (fork)
|
||||
================
|
||||
|
||||
Decoder code based on package encoding/json from the Go language.
|
||||
|
||||
@@ -1189,82 +1160,6 @@ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
github.com/smartystreets/goconvey/LICENSE.md
|
||||
================
|
||||
|
||||
Copyright (c) 2016 SmartyStreets, LLC
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
NOTE: Various optional and subordinate components carry their own licensing
|
||||
requirements and restrictions. Use of those components is subject to the terms
|
||||
and conditions outlined the respective license of each component.
|
||||
|
||||
github.com/smartystreets/assertions/LICENSE.md
|
||||
================
|
||||
|
||||
Copyright (c) 2016 SmartyStreets, LLC
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
NOTE: Various optional and subordinate components carry their own licensing
|
||||
requirements and restrictions. Use of those components is subject to the terms
|
||||
and conditions outlined the respective license of each component.
|
||||
|
||||
github.com/jtolds/gls/LICENSE
|
||||
================
|
||||
|
||||
Copyright (c) 2013, Space Monkey, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
github.com/npat-efault/poller/LICENSE.txt
|
||||
================
|
||||
github.com/zyedidia/poller/LICENSE.txt (fork)
|
||||
@@ -1362,10 +1257,35 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
github.com/lucasb-eyer/go-colorful/LICENSE
|
||||
================
|
||||
github.com/kballard/go-shellquote/LICENSE
|
||||
===============
|
||||
|
||||
Copyright (c) 2013 Lucas Beyer
|
||||
Copyright (C) 2014 Kevin Ballard
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
github.com/stretchr/testify
|
||||
=================
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2012-2018 Mat Ryer and Tyler Bunnell
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -1374,8 +1294,8 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
|
||||
@@ -167,10 +167,11 @@ by pressing CtrlE in micro and typing `set colorscheme simple`.
|
||||
If you are using the default Ubuntu terminal, to enable 256 make sure your `TERM` variable is set
|
||||
to `xterm-256color`.
|
||||
|
||||
Many of the Windows terminals don't support more than 16 colors, which means
|
||||
Many older Windows terminals don't support more than 16 colors, which means
|
||||
that micro's default colorscheme won't look very good. You can either set
|
||||
the colorscheme to `simple`, or download a better terminal emulator, like
|
||||
mintty.
|
||||
mintty. However, if you are on a recent version of Windows 10, the default
|
||||
`cmd.exe` should do fine.
|
||||
|
||||
### Plan9, Cygwin, Mingw
|
||||
|
||||
|
||||
143
cmd/micro/clean.go
Normal file
143
cmd/micro/clean.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/zyedidia/micro/internal/buffer"
|
||||
"github.com/zyedidia/micro/internal/config"
|
||||
)
|
||||
|
||||
func shouldContinue() bool {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
fmt.Print("Continue [Y/n]: ")
|
||||
text, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return false
|
||||
}
|
||||
|
||||
if len(text) <= 1 {
|
||||
// default continue
|
||||
return true
|
||||
}
|
||||
|
||||
return strings.ToLower(text)[0] == 'y'
|
||||
}
|
||||
|
||||
// CleanConfig performs cleanup in the user's configuration directory
|
||||
func CleanConfig() {
|
||||
fmt.Println("Cleaning your configuration directory at", config.ConfigDir)
|
||||
fmt.Printf("Please consider backing up %s before continuing\n", config.ConfigDir)
|
||||
|
||||
if !shouldContinue() {
|
||||
fmt.Println("Stopping early")
|
||||
return
|
||||
}
|
||||
|
||||
// detect unused options
|
||||
var unusedOptions []string
|
||||
defaultSettings := config.DefaultAllSettings()
|
||||
for k := range config.GlobalSettings {
|
||||
if _, ok := defaultSettings[k]; !ok {
|
||||
valid := false
|
||||
for _, p := range config.Plugins {
|
||||
if strings.HasPrefix(k, p.Name+".") || k == p.Name {
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
unusedOptions = append(unusedOptions, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(unusedOptions) > 0 {
|
||||
fmt.Println("The following options are unused:")
|
||||
|
||||
sort.Strings(unusedOptions)
|
||||
|
||||
for _, s := range unusedOptions {
|
||||
fmt.Printf("%s (value: %v)\n", s, config.GlobalSettings[s])
|
||||
}
|
||||
|
||||
fmt.Printf("These options will be removed from %s\n", filepath.Join(config.ConfigDir, "settings.json"))
|
||||
|
||||
if shouldContinue() {
|
||||
for _, s := range unusedOptions {
|
||||
delete(config.GlobalSettings, s)
|
||||
}
|
||||
|
||||
err := config.OverwriteSettings(filepath.Join(config.ConfigDir, "settings.json"))
|
||||
if err != nil {
|
||||
fmt.Println("Error writing settings.json file: " + err.Error())
|
||||
}
|
||||
|
||||
fmt.Println("Removed unused options")
|
||||
fmt.Print("\n\n")
|
||||
}
|
||||
}
|
||||
|
||||
// detect incorrectly formatted buffer/ files
|
||||
files, err := ioutil.ReadDir(filepath.Join(config.ConfigDir, "buffers"))
|
||||
if err == nil {
|
||||
var badFiles []string
|
||||
var buffer buffer.SerializedBuffer
|
||||
for _, f := range files {
|
||||
fname := filepath.Join(config.ConfigDir, "buffers", f.Name())
|
||||
file, e := os.Open(fname)
|
||||
|
||||
if e == nil {
|
||||
defer file.Close()
|
||||
|
||||
decoder := gob.NewDecoder(file)
|
||||
err = decoder.Decode(&buffer)
|
||||
|
||||
if err != nil && f.Name() != "history" {
|
||||
badFiles = append(badFiles, fname)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(badFiles) > 0 {
|
||||
fmt.Printf("Detected %d files with an invalid format in %s\n", len(badFiles), filepath.Join(config.ConfigDir, "buffers"))
|
||||
fmt.Println("These files store cursor and undo history.")
|
||||
fmt.Printf("Removing badly formatted files in %s\n", filepath.Join(config.ConfigDir, "buffers"))
|
||||
|
||||
if shouldContinue() {
|
||||
for _, f := range badFiles {
|
||||
err := os.Remove(f)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("Removed badly formatted files")
|
||||
fmt.Print("\n\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// detect plugins/ directory
|
||||
plugins := filepath.Join(config.ConfigDir, "plugins")
|
||||
if stat, err := os.Stat(plugins); err == nil && stat.IsDir() {
|
||||
fmt.Printf("Found directory %s\n", plugins)
|
||||
fmt.Printf("Plugins should now be stored in %s\n", filepath.Join(config.ConfigDir, "plug"))
|
||||
fmt.Printf("Removing %s\n", plugins)
|
||||
|
||||
if shouldContinue() {
|
||||
os.RemoveAll(plugins)
|
||||
}
|
||||
|
||||
fmt.Print("\n\n")
|
||||
}
|
||||
|
||||
fmt.Println("Done cleaning")
|
||||
}
|
||||
@@ -21,6 +21,7 @@ func init() {
|
||||
ulua.L.SetGlobal("import", luar.New(ulua.L, LuaImport))
|
||||
}
|
||||
|
||||
// LuaImport is meant to be called from lua by a plugin and will import the given micro package
|
||||
func LuaImport(pkg string) *lua.LTable {
|
||||
switch pkg {
|
||||
case "micro":
|
||||
@@ -46,6 +47,12 @@ func luaImportMicro() *lua.LTable {
|
||||
ulua.L.SetField(pkg, "InfoBar", luar.New(ulua.L, action.GetInfoBar))
|
||||
ulua.L.SetField(pkg, "Log", luar.New(ulua.L, log.Println))
|
||||
ulua.L.SetField(pkg, "SetStatusInfoFn", luar.New(ulua.L, display.SetStatusInfoFnLua))
|
||||
ulua.L.SetField(pkg, "CurPane", luar.New(ulua.L, func() action.Pane {
|
||||
return action.MainTab().CurPane()
|
||||
}))
|
||||
ulua.L.SetField(pkg, "CurTab", luar.New(ulua.L, func() *action.Tab {
|
||||
return action.MainTab()
|
||||
}))
|
||||
|
||||
return pkg
|
||||
}
|
||||
@@ -53,7 +60,7 @@ func luaImportMicro() *lua.LTable {
|
||||
func luaImportMicroConfig() *lua.LTable {
|
||||
pkg := ulua.L.NewTable()
|
||||
|
||||
ulua.L.SetField(pkg, "MakeCommand", luar.New(ulua.L, action.LuaMakeCommand))
|
||||
ulua.L.SetField(pkg, "MakeCommand", luar.New(ulua.L, action.MakeCommand))
|
||||
ulua.L.SetField(pkg, "FileComplete", luar.New(ulua.L, buffer.FileComplete))
|
||||
ulua.L.SetField(pkg, "HelpComplete", luar.New(ulua.L, action.HelpComplete))
|
||||
ulua.L.SetField(pkg, "OptionComplete", luar.New(ulua.L, action.OptionComplete))
|
||||
@@ -62,16 +69,17 @@ func luaImportMicroConfig() *lua.LTable {
|
||||
ulua.L.SetField(pkg, "TryBindKey", luar.New(ulua.L, action.TryBindKey))
|
||||
ulua.L.SetField(pkg, "Reload", luar.New(ulua.L, action.ReloadConfig))
|
||||
ulua.L.SetField(pkg, "AddRuntimeFileFromMemory", luar.New(ulua.L, config.PluginAddRuntimeFileFromMemory))
|
||||
ulua.L.SetField(pkg, "AddRuntimeFilesFromDirectory", luar.New(ulua.L, config.PluginAddRuntimeFileFromMemory))
|
||||
ulua.L.SetField(pkg, "AddRuntimeFilesFromDirectory", luar.New(ulua.L, config.PluginAddRuntimeFilesFromDirectory))
|
||||
ulua.L.SetField(pkg, "AddRuntimeFile", luar.New(ulua.L, config.PluginAddRuntimeFile))
|
||||
ulua.L.SetField(pkg, "ListRuntimeFiles", luar.New(ulua.L, config.PluginListRuntimeFiles))
|
||||
ulua.L.SetField(pkg, "ReadRuntimeFile", luar.New(ulua.L, config.PluginReadRuntimeFile))
|
||||
ulua.L.SetField(pkg, "NewRTFiletype", luar.New(ulua.L, config.NewRTFiletype))
|
||||
ulua.L.SetField(pkg, "RTColorscheme", luar.New(ulua.L, config.RTColorscheme))
|
||||
ulua.L.SetField(pkg, "RTSyntax", luar.New(ulua.L, config.RTSyntax))
|
||||
ulua.L.SetField(pkg, "RTHelp", luar.New(ulua.L, config.RTHelp))
|
||||
ulua.L.SetField(pkg, "RTPlugin", luar.New(ulua.L, config.RTPlugin))
|
||||
ulua.L.SetField(pkg, "RegisterCommonOption", luar.New(ulua.L, config.RegisterCommonOption))
|
||||
ulua.L.SetField(pkg, "RegisterGlobalOption", luar.New(ulua.L, config.RegisterGlobalOption))
|
||||
ulua.L.SetField(pkg, "RegisterCommonOption", luar.New(ulua.L, config.RegisterCommonOptionPlug))
|
||||
ulua.L.SetField(pkg, "RegisterGlobalOption", luar.New(ulua.L, config.RegisterGlobalOptionPlug))
|
||||
ulua.L.SetField(pkg, "GetGlobalOption", luar.New(ulua.L, config.GetGlobalOption))
|
||||
ulua.L.SetField(pkg, "SetGlobalOption", luar.New(ulua.L, action.SetGlobalOption))
|
||||
ulua.L.SetField(pkg, "SetGlobalOptionNative", luar.New(ulua.L, action.SetGlobalOptionNative))
|
||||
@@ -113,6 +121,9 @@ func luaImportMicroBuffer() *lua.LTable {
|
||||
ulua.L.SetField(pkg, "BTScratch", luar.New(ulua.L, buffer.BTScratch.Kind))
|
||||
ulua.L.SetField(pkg, "BTRaw", luar.New(ulua.L, buffer.BTRaw.Kind))
|
||||
ulua.L.SetField(pkg, "BTInfo", luar.New(ulua.L, buffer.BTInfo.Kind))
|
||||
ulua.L.SetField(pkg, "NewBuffer", luar.New(ulua.L, func(text, path string) *buffer.Buffer {
|
||||
return buffer.NewBufferFromString(text, path, buffer.BTDefault)
|
||||
}))
|
||||
ulua.L.SetField(pkg, "NewBufferFromFile", luar.New(ulua.L, func(path string) (*buffer.Buffer, error) {
|
||||
return buffer.NewBufferFromFile(path, buffer.BTDefault)
|
||||
}))
|
||||
@@ -129,6 +140,10 @@ func luaImportMicroUtil() *lua.LTable {
|
||||
ulua.L.SetField(pkg, "RuneAt", luar.New(ulua.L, util.LuaRuneAt))
|
||||
ulua.L.SetField(pkg, "GetLeadingWhitespace", luar.New(ulua.L, util.LuaGetLeadingWhitespace))
|
||||
ulua.L.SetField(pkg, "IsWordChar", luar.New(ulua.L, util.LuaIsWordChar))
|
||||
ulua.L.SetField(pkg, "String", luar.New(ulua.L, util.String))
|
||||
ulua.L.SetField(pkg, "RuneStr", luar.New(ulua.L, func(r rune) string {
|
||||
return string(r)
|
||||
}))
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
isatty "github.com/mattn/go-isatty"
|
||||
@@ -29,17 +30,20 @@ var (
|
||||
flagConfigDir = flag.String("config-dir", "", "Specify a custom location for the configuration directory")
|
||||
flagOptions = flag.Bool("options", false, "Show all option help")
|
||||
flagDebug = flag.Bool("debug", false, "Enable debug mode (prints debug info to ./log.txt)")
|
||||
flagPlugin = flag.String("plugin", "", "Plugin command")
|
||||
flagClean = flag.Bool("clean", false, "Clean configuration directory")
|
||||
optionFlags map[string]*string
|
||||
)
|
||||
|
||||
func InitFlags() {
|
||||
flag.Usage = func() {
|
||||
fmt.Println("Usage: micro [OPTIONS] [FILE]...")
|
||||
fmt.Println("-clean")
|
||||
fmt.Println(" \tCleans the configuration directory")
|
||||
fmt.Println("-config-dir dir")
|
||||
fmt.Println(" \tSpecify a custom location for the configuration directory")
|
||||
fmt.Println("[FILE]:LINE:COL")
|
||||
fmt.Println(" \tSpecify a line and column to start the cursor at when opening a buffer")
|
||||
fmt.Println(" \tThis can also be done by opening file:LINE:COL")
|
||||
fmt.Println("-options")
|
||||
fmt.Println(" \tShow all option help")
|
||||
fmt.Println("-debug")
|
||||
@@ -47,6 +51,20 @@ func InitFlags() {
|
||||
fmt.Println("-version")
|
||||
fmt.Println(" \tShow the version number and information")
|
||||
|
||||
fmt.Print("\nMicro's plugin's can be managed at the command line with the following commands.\n")
|
||||
fmt.Println("-plugin install [PLUGIN]...")
|
||||
fmt.Println(" \tInstall plugin(s)")
|
||||
fmt.Println("-plugin remove [PLUGIN]...")
|
||||
fmt.Println(" \tRemove plugin(s)")
|
||||
fmt.Println("-plugin update [PLUGIN]...")
|
||||
fmt.Println(" \tUpdate plugin(s) (if no argument is given, updates all plugins)")
|
||||
fmt.Println("-plugin search [PLUGIN]...")
|
||||
fmt.Println(" \tSearch for a plugin")
|
||||
fmt.Println("-plugin list")
|
||||
fmt.Println(" \tList installed plugins")
|
||||
fmt.Println("-plugin available")
|
||||
fmt.Println(" \tList available plugins")
|
||||
|
||||
fmt.Print("\nMicro's options can also be set via command line arguments for quick\nadjustments. For real configuration, please use the settings.json\nfile (see 'help options').\n\n")
|
||||
fmt.Println("-option value")
|
||||
fmt.Println(" \tSet `option` to `value` for this session")
|
||||
@@ -91,6 +109,23 @@ func InitFlags() {
|
||||
}
|
||||
}
|
||||
|
||||
// DoPluginFlags parses and executes any flags that require LoadAllPlugins (-plugin and -clean)
|
||||
func DoPluginFlags() {
|
||||
if *flagClean || *flagPlugin != "" {
|
||||
config.LoadAllPlugins()
|
||||
|
||||
if *flagPlugin != "" {
|
||||
args := flag.Args()
|
||||
|
||||
config.PluginCommand(os.Stdout, *flagPlugin, args)
|
||||
} else if *flagClean {
|
||||
CleanConfig()
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadInput determines which files should be loaded into buffers
|
||||
// based on the input stored in flag.Args()
|
||||
func LoadInput() []*buffer.Buffer {
|
||||
@@ -179,6 +214,8 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
DoPluginFlags()
|
||||
|
||||
screen.Init()
|
||||
|
||||
// If we have an error, we can exit cleanly and not completely
|
||||
@@ -198,19 +235,15 @@ func main() {
|
||||
}
|
||||
}()
|
||||
|
||||
action.InitBindings()
|
||||
action.InitCommands()
|
||||
|
||||
err = config.InitColorscheme()
|
||||
if err != nil {
|
||||
screen.TermMessage(err)
|
||||
}
|
||||
|
||||
err = config.LoadAllPlugins()
|
||||
if err != nil {
|
||||
screen.TermMessage(err)
|
||||
}
|
||||
err = config.RunPluginFn("init")
|
||||
|
||||
action.InitBindings()
|
||||
action.InitCommands()
|
||||
|
||||
err = config.InitColorscheme()
|
||||
if err != nil {
|
||||
screen.TermMessage(err)
|
||||
}
|
||||
@@ -226,6 +259,11 @@ func main() {
|
||||
action.InitTabs(b)
|
||||
action.InitGlobals()
|
||||
|
||||
err = config.RunPluginFn("init")
|
||||
if err != nil {
|
||||
screen.TermMessage(err)
|
||||
}
|
||||
|
||||
events = make(chan tcell.Event)
|
||||
|
||||
// Here is the event loop which runs in a separate thread
|
||||
@@ -240,6 +278,22 @@ func main() {
|
||||
}
|
||||
}()
|
||||
|
||||
// clear the drawchan so we don't redraw excessively
|
||||
// if someone requested a redraw before we started displaying
|
||||
for len(screen.DrawChan) > 0 {
|
||||
<-screen.DrawChan
|
||||
}
|
||||
|
||||
var event tcell.Event
|
||||
|
||||
// wait for initial resize event
|
||||
select {
|
||||
case event = <-events:
|
||||
action.Tabs.HandleEvent(event)
|
||||
case <-time.After(10 * time.Millisecond):
|
||||
// time out after 10ms
|
||||
}
|
||||
|
||||
for {
|
||||
// Display everything
|
||||
screen.Screen.Fill(' ', config.DefStyle)
|
||||
@@ -252,13 +306,13 @@ func main() {
|
||||
action.InfoBar.Display()
|
||||
screen.Screen.Show()
|
||||
|
||||
var event tcell.Event
|
||||
event = nil
|
||||
|
||||
// Check for new events
|
||||
select {
|
||||
case f := <-shell.Jobs:
|
||||
// If a new job has finished while running in the background we should execute the callback
|
||||
f.Function(f.Output, f.Args...)
|
||||
f.Function(f.Output, f.Args)
|
||||
case <-config.Autosave:
|
||||
for _, b := range buffer.OpenBuffers {
|
||||
b.Save()
|
||||
|
||||
4
go.sum
4
go.sum
@@ -50,10 +50,6 @@ github.com/zyedidia/poller v1.0.1 h1:Tt9S3AxAjXwWGNiC2TUdRJkQDZSzCBNVQ4xXiQ7440s
|
||||
github.com/zyedidia/poller v1.0.1/go.mod h1:vZXJOHGDcuK08GXhF6IAY0ZFd2WcgOR5DOTp84Uk5eE=
|
||||
github.com/zyedidia/pty v2.0.0+incompatible h1:Ou5vXL6tvjst+RV8sUFISbuKDnUJPhnpygApMFGweqw=
|
||||
github.com/zyedidia/pty v2.0.0+incompatible/go.mod h1:4y9l9yJZNxRa7GB/fB+mmDmGkG3CqmzLf4vUxGGotEA=
|
||||
github.com/zyedidia/tcell v1.4.0 h1:uhAz+bdB3HHlVP2hff3WURkI+pERNwgVfy27oi1Gb2A=
|
||||
github.com/zyedidia/tcell v1.4.0/go.mod h1:HhlbMSCcGX15rFDB+Q1Lk3pKEOocsCUAQC3zhZ9sadA=
|
||||
github.com/zyedidia/tcell v1.4.1 h1:zLci8cg1SLINjwSePZ1yUWnYOnZXMyr4h+zaOvhu5K8=
|
||||
github.com/zyedidia/tcell v1.4.1/go.mod h1:HhlbMSCcGX15rFDB+Q1Lk3pKEOocsCUAQC3zhZ9sadA=
|
||||
github.com/zyedidia/tcell v1.4.2 h1:JWMDs6O1saINPIR5M3kNqlWJwkfnBZeZDZszEJi3BW8=
|
||||
github.com/zyedidia/tcell v1.4.2/go.mod h1:HhlbMSCcGX15rFDB+Q1Lk3pKEOocsCUAQC3zhZ9sadA=
|
||||
github.com/zyedidia/terminal v0.0.0-20180726154117-533c623e2415 h1:752dTQ5OatJ9M5ULK2+9lor+nzyZz+LYDo3WGngg3Rc=
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package action
|
||||
|
||||
import (
|
||||
"log"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -633,11 +632,9 @@ func (h *BufPane) CycleAutocompleteBack() bool {
|
||||
if h.Cursor.HasSelection() {
|
||||
return false
|
||||
}
|
||||
log.Println(h.Buf.HasSuggestions)
|
||||
|
||||
if h.Buf.HasSuggestions {
|
||||
h.Buf.CycleAutocomplete(false)
|
||||
log.Println("TRUE")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -1281,20 +1278,20 @@ func (h *BufPane) Quit() bool {
|
||||
}
|
||||
}
|
||||
if h.Buf.Modified() {
|
||||
// if config.GlobalSettings["autosave"].(float64) > 0 {
|
||||
// autosave on means we automatically save when quitting
|
||||
// h.Save()
|
||||
// quit()
|
||||
// } else {
|
||||
InfoBar.YNPrompt("Save changes to "+h.Buf.GetName()+" before closing? (y,n,esc)", func(yes, canceled bool) {
|
||||
if !canceled && !yes {
|
||||
quit()
|
||||
} else if !canceled && yes {
|
||||
h.Save()
|
||||
quit()
|
||||
}
|
||||
})
|
||||
// }
|
||||
if config.GlobalSettings["autosave"].(float64) > 0 {
|
||||
// autosave on means we automatically save when quitting
|
||||
h.Save()
|
||||
quit()
|
||||
} else {
|
||||
InfoBar.YNPrompt("Save changes to "+h.Buf.GetName()+" before closing? (y,n,esc)", func(yes, canceled bool) {
|
||||
if !canceled && !yes {
|
||||
quit()
|
||||
} else if !canceled && yes {
|
||||
h.Save()
|
||||
quit()
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
quit()
|
||||
}
|
||||
@@ -1376,38 +1373,42 @@ func (h *BufPane) HSplitAction() bool {
|
||||
|
||||
// Unsplit closes all splits in the current tab except the active one
|
||||
func (h *BufPane) Unsplit() bool {
|
||||
n := MainTab().GetNode(h.splitID)
|
||||
n.Unsplit()
|
||||
tab := h.tab
|
||||
n := tab.GetNode(h.splitID)
|
||||
ok := n.Unsplit()
|
||||
if ok {
|
||||
tab.RemovePane(tab.GetPane(h.splitID))
|
||||
tab.Resize()
|
||||
tab.SetActive(len(tab.Panes) - 1)
|
||||
|
||||
MainTab().RemovePane(MainTab().GetPane(h.splitID))
|
||||
MainTab().Resize()
|
||||
MainTab().SetActive(len(MainTab().Panes) - 1)
|
||||
return true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NextSplit changes the view to the next split
|
||||
func (h *BufPane) NextSplit() bool {
|
||||
a := MainTab().active
|
||||
if a < len(MainTab().Panes)-1 {
|
||||
a := h.tab.active
|
||||
if a < len(h.tab.Panes)-1 {
|
||||
a++
|
||||
} else {
|
||||
a = 0
|
||||
}
|
||||
|
||||
MainTab().SetActive(a)
|
||||
h.tab.SetActive(a)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// PreviousSplit changes the view to the previous split
|
||||
func (h *BufPane) PreviousSplit() bool {
|
||||
a := MainTab().active
|
||||
a := h.tab.active
|
||||
if a > 0 {
|
||||
a--
|
||||
} else {
|
||||
a = len(MainTab().Panes) - 1
|
||||
a = len(h.tab.Panes) - 1
|
||||
}
|
||||
MainTab().SetActive(a)
|
||||
h.tab.SetActive(a)
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1486,6 +1487,41 @@ func (h *BufPane) SpawnMultiCursor() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// SpawnMultiCursorUp creates additional cursor, at the same X (if possible), one Y less.
|
||||
func (h *BufPane) SpawnMultiCursorUp() bool {
|
||||
if h.Cursor.Y == 0 {
|
||||
return false
|
||||
} else {
|
||||
h.Cursor.GotoLoc(buffer.Loc{h.Cursor.X, h.Cursor.Y - 1})
|
||||
h.Cursor.Relocate()
|
||||
}
|
||||
|
||||
c := buffer.NewCursor(h.Buf, buffer.Loc{h.Cursor.X, h.Cursor.Y + 1})
|
||||
h.Buf.AddCursor(c)
|
||||
h.Buf.SetCurCursor(h.Buf.NumCursors() - 1)
|
||||
h.Buf.MergeCursors()
|
||||
|
||||
h.Relocate()
|
||||
return true
|
||||
}
|
||||
|
||||
// SpawnMultiCursorUp creates additional cursor, at the same X (if possible), one Y more.
|
||||
func (h *BufPane) SpawnMultiCursorDown() bool {
|
||||
if h.Cursor.Y+1 == h.Buf.LinesNum() {
|
||||
return false
|
||||
} else {
|
||||
h.Cursor.GotoLoc(buffer.Loc{h.Cursor.X, h.Cursor.Y + 1})
|
||||
h.Cursor.Relocate()
|
||||
}
|
||||
|
||||
c := buffer.NewCursor(h.Buf, buffer.Loc{h.Cursor.X, h.Cursor.Y - 1})
|
||||
h.Buf.AddCursor(c)
|
||||
h.Buf.SetCurCursor(h.Buf.NumCursors() - 1)
|
||||
h.Buf.MergeCursors()
|
||||
h.Relocate()
|
||||
return true
|
||||
}
|
||||
|
||||
// SpawnMultiCursorSelect adds a cursor at the beginning of each line of a selection
|
||||
func (h *BufPane) SpawnMultiCursorSelect() bool {
|
||||
// Avoid cases where multiple cursors already exist, that would create problems
|
||||
|
||||
@@ -91,7 +91,14 @@ func BufMapKey(k Event, action string) {
|
||||
screen.TermMessage("Lua Error:", a, "does not exist")
|
||||
continue
|
||||
}
|
||||
names = append(names, "")
|
||||
split := strings.SplitN(a, ".", 2)
|
||||
if len(split) > 1 {
|
||||
a = strings.Title(split[0]) + strings.Title(split[1])
|
||||
} else {
|
||||
a = strings.Title(a)
|
||||
}
|
||||
|
||||
names = append(names, a)
|
||||
} else if f, ok := BufKeyActions[a]; ok {
|
||||
afn = f
|
||||
names = append(names, a)
|
||||
@@ -175,15 +182,17 @@ type BufPane struct {
|
||||
multiWord bool
|
||||
|
||||
splitID uint64
|
||||
tab *Tab
|
||||
|
||||
// remember original location of a search in case the search is canceled
|
||||
searchOrig buffer.Loc
|
||||
}
|
||||
|
||||
func NewBufPane(buf *buffer.Buffer, win display.BWindow) *BufPane {
|
||||
func NewBufPane(buf *buffer.Buffer, win display.BWindow, tab *Tab) *BufPane {
|
||||
h := new(BufPane)
|
||||
h.Buf = buf
|
||||
h.BWindow = win
|
||||
h.tab = tab
|
||||
|
||||
h.Cursor = h.Buf.GetActiveCursor()
|
||||
h.mouseReleased = true
|
||||
@@ -193,9 +202,23 @@ func NewBufPane(buf *buffer.Buffer, win display.BWindow) *BufPane {
|
||||
return h
|
||||
}
|
||||
|
||||
func NewBufPaneFromBuf(buf *buffer.Buffer) *BufPane {
|
||||
func NewBufPaneFromBuf(buf *buffer.Buffer, tab *Tab) *BufPane {
|
||||
w := display.NewBufWindow(0, 0, 0, 0, buf)
|
||||
return NewBufPane(buf, w)
|
||||
return NewBufPane(buf, w, tab)
|
||||
}
|
||||
|
||||
func (h *BufPane) SetTab(t *Tab) {
|
||||
h.tab = t
|
||||
}
|
||||
|
||||
func (h *BufPane) Tab() *Tab {
|
||||
return h.tab
|
||||
}
|
||||
|
||||
func (h *BufPane) ResizePane(size int) {
|
||||
n := h.tab.GetNode(h.splitID)
|
||||
n.ResizeSplit(size)
|
||||
h.tab.Resize()
|
||||
}
|
||||
|
||||
// PluginCB calls all plugin callbacks with a certain name and
|
||||
@@ -433,22 +456,29 @@ func (h *BufPane) DoRuneInsert(r rune) {
|
||||
}
|
||||
}
|
||||
|
||||
func (h *BufPane) VSplitBuf(buf *buffer.Buffer) *BufPane {
|
||||
e := NewBufPaneFromBuf(buf)
|
||||
e.splitID = MainTab().GetNode(h.splitID).VSplit(h.Buf.Settings["splitright"].(bool))
|
||||
func (h *BufPane) VSplitIndex(buf *buffer.Buffer, right bool) *BufPane {
|
||||
e := NewBufPaneFromBuf(buf, h.tab)
|
||||
e.splitID = MainTab().GetNode(h.splitID).VSplit(right)
|
||||
MainTab().Panes = append(MainTab().Panes, e)
|
||||
MainTab().Resize()
|
||||
MainTab().SetActive(len(MainTab().Panes) - 1)
|
||||
return e
|
||||
}
|
||||
func (h *BufPane) HSplitBuf(buf *buffer.Buffer) *BufPane {
|
||||
e := NewBufPaneFromBuf(buf)
|
||||
e.splitID = MainTab().GetNode(h.splitID).HSplit(h.Buf.Settings["splitbottom"].(bool))
|
||||
func (h *BufPane) HSplitIndex(buf *buffer.Buffer, bottom bool) *BufPane {
|
||||
e := NewBufPaneFromBuf(buf, h.tab)
|
||||
e.splitID = MainTab().GetNode(h.splitID).HSplit(bottom)
|
||||
MainTab().Panes = append(MainTab().Panes, e)
|
||||
MainTab().Resize()
|
||||
MainTab().SetActive(len(MainTab().Panes) - 1)
|
||||
return e
|
||||
}
|
||||
|
||||
func (h *BufPane) VSplitBuf(buf *buffer.Buffer) *BufPane {
|
||||
return h.VSplitIndex(buf, h.Buf.Settings["splitright"].(bool))
|
||||
}
|
||||
func (h *BufPane) HSplitBuf(buf *buffer.Buffer) *BufPane {
|
||||
return h.HSplitIndex(buf, h.Buf.Settings["splitbottom"].(bool))
|
||||
}
|
||||
func (h *BufPane) Close() {
|
||||
h.Buf.Close()
|
||||
}
|
||||
@@ -565,6 +595,8 @@ var BufKeyActions = map[string]BufKeyAction{
|
||||
"ScrollUp": (*BufPane).ScrollUpAction,
|
||||
"ScrollDown": (*BufPane).ScrollDownAction,
|
||||
"SpawnMultiCursor": (*BufPane).SpawnMultiCursor,
|
||||
"SpawnMultiCursorUp": (*BufPane).SpawnMultiCursorUp,
|
||||
"SpawnMultiCursorDown": (*BufPane).SpawnMultiCursorDown,
|
||||
"SpawnMultiCursorSelect": (*BufPane).SpawnMultiCursorSelect,
|
||||
"RemoveMultiCursor": (*BufPane).RemoveMultiCursor,
|
||||
"RemoveAllMultiCursors": (*BufPane).RemoveAllMultiCursors,
|
||||
@@ -609,6 +641,7 @@ var MultiActions = map[string]bool{
|
||||
"DeleteWordLeft": true,
|
||||
"SelectLine": true,
|
||||
"SelectToStartOfLine": true,
|
||||
"SelectToStartOfText": true,
|
||||
"SelectToEndOfLine": true,
|
||||
"ParagraphPrevious": true,
|
||||
"ParagraphNext": true,
|
||||
@@ -632,6 +665,7 @@ var MultiActions = map[string]bool{
|
||||
"SelectPageUp": true,
|
||||
"SelectPageDown": true,
|
||||
"StartOfLine": true,
|
||||
"StartOfText": true,
|
||||
"EndOfLine": true,
|
||||
"JumpToMatchingBrace": true,
|
||||
}
|
||||
|
||||
@@ -12,13 +12,9 @@ import (
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
luar "layeh.com/gopher-luar"
|
||||
|
||||
shellquote "github.com/kballard/go-shellquote"
|
||||
lua "github.com/yuin/gopher-lua"
|
||||
"github.com/zyedidia/micro/internal/buffer"
|
||||
"github.com/zyedidia/micro/internal/config"
|
||||
ulua "github.com/zyedidia/micro/internal/lua"
|
||||
"github.com/zyedidia/micro/internal/screen"
|
||||
"github.com/zyedidia/micro/internal/shell"
|
||||
"github.com/zyedidia/micro/internal/util"
|
||||
@@ -71,29 +67,9 @@ func InitCommands() {
|
||||
|
||||
// MakeCommand is a function to easily create new commands
|
||||
// This can be called by plugins in Lua so that plugins can define their own commands
|
||||
func LuaMakeCommand(name, function string, completer buffer.Completer) {
|
||||
action := LuaFunctionCommand(function)
|
||||
commands[name] = Command{action, completer}
|
||||
}
|
||||
|
||||
// LuaFunctionCommand returns a normal function
|
||||
// so that a command can be bound to a lua function
|
||||
func LuaFunctionCommand(fn string) func(*BufPane, []string) {
|
||||
luaFn := strings.Split(fn, ".")
|
||||
if len(luaFn) <= 1 {
|
||||
return nil
|
||||
}
|
||||
plName, plFn := luaFn[0], luaFn[1]
|
||||
pl := config.FindPlugin(plName)
|
||||
if pl == nil {
|
||||
return nil
|
||||
}
|
||||
return func(bp *BufPane, args []string) {
|
||||
luaArgs := []lua.LValue{luar.New(ulua.L, bp), luar.New(ulua.L, args)}
|
||||
_, err := pl.Call(plFn, luaArgs...)
|
||||
if err != nil {
|
||||
screen.TermMessage(err)
|
||||
}
|
||||
func MakeCommand(name string, action func(bp *BufPane, args []string), completer buffer.Completer) {
|
||||
if action != nil {
|
||||
commands[name] = Command{action, completer}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,106 +96,20 @@ func CommandAction(cmd string) BufKeyAction {
|
||||
}
|
||||
}
|
||||
|
||||
var PluginCmds = []string{"list", "info", "version"}
|
||||
var PluginCmds = []string{"install", "remove", "update", "available", "list", "search"}
|
||||
|
||||
// PluginCmd installs, removes, updates, lists, or searches for given plugins
|
||||
func (h *BufPane) PluginCmd(args []string) {
|
||||
if len(args) <= 0 {
|
||||
InfoBar.Error("Not enough arguments, see 'help commands'")
|
||||
if len(args) < 1 {
|
||||
InfoBar.Error("Not enough arguments")
|
||||
return
|
||||
}
|
||||
|
||||
valid := true
|
||||
switch args[0] {
|
||||
case "list":
|
||||
for _, pl := range config.Plugins {
|
||||
var en string
|
||||
if pl.IsEnabled() {
|
||||
en = "enabled"
|
||||
} else {
|
||||
en = "disabled"
|
||||
}
|
||||
WriteLog(fmt.Sprintf("%s: %s", pl.Name, en))
|
||||
if pl.Default {
|
||||
WriteLog(" (default)\n")
|
||||
} else {
|
||||
WriteLog("\n")
|
||||
}
|
||||
}
|
||||
WriteLog("Default plugins come pre-installed with micro.")
|
||||
case "version":
|
||||
if len(args) <= 1 {
|
||||
InfoBar.Error("No plugin provided to give info for")
|
||||
return
|
||||
}
|
||||
found := false
|
||||
for _, pl := range config.Plugins {
|
||||
if pl.Name == args[1] {
|
||||
found = true
|
||||
if pl.Info == nil {
|
||||
InfoBar.Message("Sorry no version for", pl.Name)
|
||||
return
|
||||
}
|
||||
|
||||
WriteLog("Version: " + pl.Info.Vstr + "\n")
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
InfoBar.Message(args[1], "is not installed")
|
||||
}
|
||||
case "info":
|
||||
if len(args) <= 1 {
|
||||
InfoBar.Error("No plugin provided to give info for")
|
||||
return
|
||||
}
|
||||
found := false
|
||||
for _, pl := range config.Plugins {
|
||||
if pl.Name == args[1] {
|
||||
found = true
|
||||
if pl.Info == nil {
|
||||
InfoBar.Message("Sorry no info for ", pl.Name)
|
||||
return
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteString("Name: ")
|
||||
buffer.WriteString(pl.Info.Name)
|
||||
buffer.WriteString("\n")
|
||||
buffer.WriteString("Description: ")
|
||||
buffer.WriteString(pl.Info.Desc)
|
||||
buffer.WriteString("\n")
|
||||
buffer.WriteString("Website: ")
|
||||
buffer.WriteString(pl.Info.Site)
|
||||
buffer.WriteString("\n")
|
||||
buffer.WriteString("Installation link: ")
|
||||
buffer.WriteString(pl.Info.Install)
|
||||
buffer.WriteString("\n")
|
||||
buffer.WriteString("Version: ")
|
||||
buffer.WriteString(pl.Info.Vstr)
|
||||
buffer.WriteString("\n")
|
||||
buffer.WriteString("Requirements:")
|
||||
buffer.WriteString("\n")
|
||||
for _, r := range pl.Info.Require {
|
||||
buffer.WriteString(" - ")
|
||||
buffer.WriteString(r)
|
||||
buffer.WriteString("\n")
|
||||
}
|
||||
|
||||
WriteLog(buffer.String())
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
InfoBar.Message(args[1], "is not installed")
|
||||
return
|
||||
}
|
||||
default:
|
||||
InfoBar.Error("Not a valid plugin command")
|
||||
return
|
||||
}
|
||||
|
||||
if valid && h.Buf.Type != buffer.BTLog {
|
||||
if h.Buf.Type != buffer.BTLog {
|
||||
OpenLogBuf(h)
|
||||
}
|
||||
|
||||
config.PluginCommand(buffer.LogBuf, args[0], args[1:])
|
||||
}
|
||||
|
||||
// RetabCmd changes all spaces to tabs or all tabs to spaces
|
||||
@@ -233,7 +123,7 @@ func (h *BufPane) RetabCmd(args []string) {
|
||||
func (h *BufPane) RawCmd(args []string) {
|
||||
width, height := screen.Screen.Size()
|
||||
iOffset := config.GetInfoBarOffset()
|
||||
tp := NewTabFromPane(0, 0, width, height-iOffset, NewRawPane())
|
||||
tp := NewTabFromPane(0, 0, width, height-iOffset, NewRawPane(nil))
|
||||
Tabs.AddTab(tp)
|
||||
Tabs.SetActive(len(Tabs.List) - 1)
|
||||
}
|
||||
@@ -552,14 +442,13 @@ func SetGlobalOptionNative(option string, nativeValue interface{}) error {
|
||||
} else {
|
||||
screen.Screen.EnableMouse()
|
||||
}
|
||||
// autosave option has been removed
|
||||
// } else if option == "autosave" {
|
||||
// if nativeValue.(float64) > 0 {
|
||||
// config.SetAutoTime(int(nativeValue.(float64)))
|
||||
// config.StartAutoSave()
|
||||
// } else {
|
||||
// config.SetAutoTime(0)
|
||||
// }
|
||||
} else if option == "autosave" {
|
||||
if nativeValue.(float64) > 0 {
|
||||
config.SetAutoTime(int(nativeValue.(float64)))
|
||||
config.StartAutoSave()
|
||||
} else {
|
||||
config.SetAutoTime(0)
|
||||
}
|
||||
} else if option == "paste" {
|
||||
screen.Screen.SetPaste(nativeValue.(bool))
|
||||
} else {
|
||||
@@ -916,7 +805,7 @@ func (h *BufPane) ReplaceAllCmd(args []string) {
|
||||
|
||||
// TermCmd opens a terminal in the current view
|
||||
func (h *BufPane) TermCmd(args []string) {
|
||||
ps := MainTab().Panes
|
||||
ps := h.tab.Panes
|
||||
|
||||
if len(args) == 0 {
|
||||
sh := os.Getenv("SHELL")
|
||||
@@ -929,7 +818,7 @@ func (h *BufPane) TermCmd(args []string) {
|
||||
|
||||
term := func(i int, newtab bool) {
|
||||
t := new(shell.Terminal)
|
||||
t.Start(args, false, true, "", nil)
|
||||
t.Start(args, false, true, nil, nil)
|
||||
|
||||
id := h.ID()
|
||||
if newtab {
|
||||
@@ -941,7 +830,7 @@ func (h *BufPane) TermCmd(args []string) {
|
||||
}
|
||||
|
||||
v := h.GetView()
|
||||
MainTab().Panes[i] = NewTermPane(v.X, v.Y, v.Width, v.Height, t, id)
|
||||
MainTab().Panes[i] = NewTermPane(v.X, v.Y, v.Width, v.Height, t, id, MainTab())
|
||||
MainTab().SetActive(i)
|
||||
}
|
||||
|
||||
|
||||
@@ -96,10 +96,12 @@ func DefaultBindings() map[string]string {
|
||||
"MouseMiddle": "PastePrimary",
|
||||
"Ctrl-MouseLeft": "MouseMultiCursor",
|
||||
|
||||
"Alt-n": "SpawnMultiCursor",
|
||||
"Alt-m": "SpawnMultiCursorSelect",
|
||||
"Alt-p": "RemoveMultiCursor",
|
||||
"Alt-c": "RemoveAllMultiCursors",
|
||||
"Alt-x": "SkipMultiCursor",
|
||||
"Alt-n": "SpawnMultiCursor",
|
||||
"AltShiftUp": "SpawnMultiCursorUp",
|
||||
"AltShiftDown": "SpawnMultiCursorDown",
|
||||
"Alt-m": "SpawnMultiCursorSelect",
|
||||
"Alt-p": "RemoveMultiCursor",
|
||||
"Alt-c": "RemoveAllMultiCursors",
|
||||
"Alt-x": "SkipMultiCursor",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,10 +98,12 @@ func DefaultBindings() map[string]string {
|
||||
"MouseMiddle": "PastePrimary",
|
||||
"Ctrl-MouseLeft": "MouseMultiCursor",
|
||||
|
||||
"Alt-n": "SpawnMultiCursor",
|
||||
"Alt-m": "SpawnMultiCursorSelect",
|
||||
"Alt-p": "RemoveMultiCursor",
|
||||
"Alt-c": "RemoveAllMultiCursors",
|
||||
"Alt-x": "SkipMultiCursor",
|
||||
"Alt-n": "SpawnMultiCursor",
|
||||
"Alt-m": "SpawnMultiCursorSelect",
|
||||
"AltShiftUp": "SpawnMultiCursorUp",
|
||||
"AltShiftDown": "SpawnMultiCursorDown",
|
||||
"Alt-p": "RemoveMultiCursor",
|
||||
"Alt-c": "RemoveAllMultiCursors",
|
||||
"Alt-x": "SkipMultiCursor",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ func OptionValueComplete(b *buffer.Buffer) ([]string, []string) {
|
||||
return completions, suggestions
|
||||
}
|
||||
|
||||
// OptionComplete autocompletes options
|
||||
// PluginCmdComplete autocompletes the plugin command
|
||||
func PluginCmdComplete(b *buffer.Buffer) ([]string, []string) {
|
||||
c := b.GetActiveCursor()
|
||||
input, argstart := buffer.GetArg(b)
|
||||
@@ -253,51 +253,28 @@ func PluginComplete(b *buffer.Buffer) ([]string, []string) {
|
||||
return completions, suggestions
|
||||
}
|
||||
|
||||
// // MakeCompletion registers a function from a plugin for autocomplete commands
|
||||
// func MakeCompletion(function string) Completion {
|
||||
// pluginCompletions = append(pluginCompletions, LuaFunctionComplete(function))
|
||||
// return Completion(-len(pluginCompletions))
|
||||
// }
|
||||
// PluginNameComplete completes with the names of loaded plugins
|
||||
// func PluginNameComplete(b *buffer.Buffer) ([]string, []string) {
|
||||
// c := b.GetActiveCursor()
|
||||
// input, argstart := buffer.GetArg(b)
|
||||
//
|
||||
// // PluginComplete autocompletes from plugin function
|
||||
// func PluginComplete(complete Completion, input string) (chosen string, suggestions []string) {
|
||||
// idx := int(-complete) - 1
|
||||
//
|
||||
// if len(pluginCompletions) <= idx {
|
||||
// return "", nil
|
||||
// }
|
||||
// suggestions = pluginCompletions[idx](input)
|
||||
//
|
||||
// if len(suggestions) == 1 {
|
||||
// chosen = suggestions[0]
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // PluginCmdComplete completes with possible choices for the `> plugin` command
|
||||
// func PluginCmdComplete(input string) (chosen string, suggestions []string) {
|
||||
// for _, cmd := range []string{"install", "remove", "search", "update", "list"} {
|
||||
// if strings.HasPrefix(cmd, input) {
|
||||
// suggestions = append(suggestions, cmd)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if len(suggestions) == 1 {
|
||||
// chosen = suggestions[0]
|
||||
// }
|
||||
// return chosen, suggestions
|
||||
// }
|
||||
//
|
||||
// // PluginnameComplete completes with the names of loaded plugins
|
||||
// func PluginNameComplete(input string) (chosen string, suggestions []string) {
|
||||
// for _, pp := range GetAllPluginPackages() {
|
||||
// var suggestions []string
|
||||
// for _, pp := range config.GetAllPluginPackages(nil) {
|
||||
// if strings.HasPrefix(pp.Name, input) {
|
||||
// suggestions = append(suggestions, pp.Name)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if len(suggestions) == 1 {
|
||||
// chosen = suggestions[0]
|
||||
// sort.Strings(suggestions)
|
||||
// completions := make([]string, len(suggestions))
|
||||
// for i := range suggestions {
|
||||
// completions[i] = util.SliceEndStr(suggestions[i], c.X-argstart)
|
||||
// }
|
||||
// return chosen, suggestions
|
||||
// return completions, suggestions
|
||||
// }
|
||||
|
||||
// // MakeCompletion registers a function from a plugin for autocomplete commands
|
||||
// func MakeCompletion(function string) Completion {
|
||||
// pluginCompletions = append(pluginCompletions, LuaFunctionComplete(function))
|
||||
// return Completion(-len(pluginCompletions))
|
||||
// }
|
||||
|
||||
@@ -17,10 +17,10 @@ type InfoPane struct {
|
||||
*info.InfoBuf
|
||||
}
|
||||
|
||||
func NewInfoPane(ib *info.InfoBuf, w display.BWindow) *InfoPane {
|
||||
func NewInfoPane(ib *info.InfoBuf, w display.BWindow, tab *Tab) *InfoPane {
|
||||
ip := new(InfoPane)
|
||||
ip.InfoBuf = ib
|
||||
ip.BufPane = NewBufPane(ib.Buffer, w)
|
||||
ip.BufPane = NewBufPane(ib.Buffer, w, tab)
|
||||
|
||||
return ip
|
||||
}
|
||||
@@ -28,7 +28,7 @@ func NewInfoPane(ib *info.InfoBuf, w display.BWindow) *InfoPane {
|
||||
func NewInfoBar() *InfoPane {
|
||||
ib := info.NewBuffer()
|
||||
w := display.NewInfoWindow(ib)
|
||||
return NewInfoPane(ib, w)
|
||||
return NewInfoPane(ib, w, nil)
|
||||
}
|
||||
|
||||
func (h *InfoPane) Close() {
|
||||
@@ -73,6 +73,7 @@ func (h *InfoPane) HandleEvent(event tcell.Event) {
|
||||
}
|
||||
}
|
||||
|
||||
// DoKeyEvent executes a key event for the command bar, doing any overridden actions
|
||||
func (h *InfoPane) DoKeyEvent(e KeyEvent) bool {
|
||||
done := false
|
||||
if action, ok := BufKeyBindings[e]; ok {
|
||||
@@ -85,7 +86,7 @@ func (h *InfoPane) DoKeyEvent(e KeyEvent) bool {
|
||||
for s, a := range InfoOverrides {
|
||||
// TODO this is a hack and really we should have support
|
||||
// for having binding overrides for different buffers
|
||||
if strings.Contains(estr, s) {
|
||||
if strings.HasPrefix(estr, s) {
|
||||
done = true
|
||||
a(h)
|
||||
break
|
||||
|
||||
@@ -11,4 +11,6 @@ type Pane interface {
|
||||
SetID(i uint64)
|
||||
Name() string
|
||||
Close()
|
||||
SetTab(t *Tab)
|
||||
Tab() *Tab
|
||||
}
|
||||
|
||||
@@ -13,17 +13,17 @@ type RawPane struct {
|
||||
*BufPane
|
||||
}
|
||||
|
||||
func NewRawPaneFromWin(b *buffer.Buffer, win display.BWindow) *RawPane {
|
||||
func NewRawPaneFromWin(b *buffer.Buffer, win display.BWindow, tab *Tab) *RawPane {
|
||||
rh := new(RawPane)
|
||||
rh.BufPane = NewBufPane(b, win)
|
||||
rh.BufPane = NewBufPane(b, win, tab)
|
||||
|
||||
return rh
|
||||
}
|
||||
|
||||
func NewRawPane() *RawPane {
|
||||
func NewRawPane(tab *Tab) *RawPane {
|
||||
b := buffer.NewBufferFromString("", "", buffer.BTRaw)
|
||||
w := display.NewBufWindow(0, 0, 0, 0, b)
|
||||
return NewRawPaneFromWin(b, w)
|
||||
return NewRawPaneFromWin(b, w, tab)
|
||||
}
|
||||
|
||||
func (h *RawPane) HandleEvent(event tcell.Event) {
|
||||
|
||||
@@ -167,7 +167,7 @@ func NewTabFromBuffer(x, y, width, height int, b *buffer.Buffer) *Tab {
|
||||
t.Node = views.NewRoot(x, y, width, height)
|
||||
t.UIWindow = display.NewUIWindow(t.Node)
|
||||
|
||||
e := NewBufPaneFromBuf(b)
|
||||
e := NewBufPaneFromBuf(b, t)
|
||||
e.SetID(t.ID())
|
||||
|
||||
t.Panes = append(t.Panes, e)
|
||||
@@ -178,7 +178,7 @@ func NewTabFromPane(x, y, width, height int, pane Pane) *Tab {
|
||||
t := new(Tab)
|
||||
t.Node = views.NewRoot(x, y, width, height)
|
||||
t.UIWindow = display.NewUIWindow(t.Node)
|
||||
|
||||
pane.SetTab(t)
|
||||
pane.SetID(t.ID())
|
||||
|
||||
t.Panes = append(t.Panes, pane)
|
||||
@@ -196,7 +196,6 @@ func (t *Tab) HandleEvent(event tcell.Event) {
|
||||
mx, my := e.Position()
|
||||
switch e.Buttons() {
|
||||
case tcell.Button1:
|
||||
resizeID := t.GetMouseSplitID(buffer.Loc{mx, my})
|
||||
if t.resizing != nil {
|
||||
var size int
|
||||
if t.resizing.Kind == views.STVert {
|
||||
@@ -209,6 +208,7 @@ func (t *Tab) HandleEvent(event tcell.Event) {
|
||||
return
|
||||
}
|
||||
|
||||
resizeID := t.GetMouseSplitID(buffer.Loc{mx, my})
|
||||
if resizeID != 0 {
|
||||
t.resizing = t.GetNode(uint64(resizeID))
|
||||
return
|
||||
@@ -284,6 +284,10 @@ func (t *Tab) Resize() {
|
||||
}
|
||||
|
||||
// CurPane returns the currently active pane
|
||||
func (t *Tab) CurPane() Pane {
|
||||
return t.Panes[t.active]
|
||||
func (t *Tab) CurPane() *BufPane {
|
||||
p, ok := t.Panes[t.active].(*BufPane)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
@@ -7,9 +7,14 @@ import (
|
||||
"github.com/zyedidia/micro/internal/shell"
|
||||
)
|
||||
|
||||
// TermEmuSupported is a constant that marks if the terminal emulator is supported
|
||||
const TermEmuSupported = true
|
||||
|
||||
func RunTermEmulator(h *BufPane, input string, wait bool, getOutput bool, callback string, userargs []interface{}) error {
|
||||
// RunTermEmulator starts a terminal emulator from a bufpane with the given input (command)
|
||||
// if wait is true it will wait for the user to exit by pressing enter once the executable has terminated
|
||||
// if getOutput is true it will redirect the stdout of the process to a pipe which will be passed to the
|
||||
// callback which is a function that takes a string and a list of optional user arguments
|
||||
func RunTermEmulator(h *BufPane, input string, wait bool, getOutput bool, callback func(out string, userargs []interface{}), userargs []interface{}) error {
|
||||
args, err := shellquote.Split(input)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -25,7 +30,7 @@ func RunTermEmulator(h *BufPane, input string, wait bool, getOutput bool, callba
|
||||
id := MainTab().Panes[0].ID()
|
||||
|
||||
v := h.GetView()
|
||||
MainTab().Panes[0] = NewTermPane(v.X, v.Y, v.Width, v.Height, t, id)
|
||||
MainTab().Panes[0] = NewTermPane(v.X, v.Y, v.Width, v.Height, t, id, MainTab())
|
||||
MainTab().SetActive(0)
|
||||
|
||||
return nil
|
||||
|
||||
@@ -4,8 +4,10 @@ package action
|
||||
|
||||
import "errors"
|
||||
|
||||
// TermEmuSupported is a constant that marks if the terminal emulator is supported
|
||||
const TermEmuSupported = false
|
||||
|
||||
func RunTermEmulator(input string, wait bool, getOutput bool, callback string, userargs []interface{}) error {
|
||||
// RunTermEmulator returns an error for unsupported systems (non-unix systems
|
||||
func RunTermEmulator(input string, wait bool, getOutput bool, callback func(out string, userargs []interface{}), userargs []interface{}) error {
|
||||
return errors.New("Unsupported operating system")
|
||||
}
|
||||
|
||||
@@ -17,14 +17,16 @@ type TermPane struct {
|
||||
|
||||
mouseReleased bool
|
||||
id uint64
|
||||
tab *Tab
|
||||
}
|
||||
|
||||
func NewTermPane(x, y, w, h int, t *shell.Terminal, id uint64) *TermPane {
|
||||
func NewTermPane(x, y, w, h int, t *shell.Terminal, id uint64, tab *Tab) *TermPane {
|
||||
th := new(TermPane)
|
||||
th.Terminal = t
|
||||
th.id = id
|
||||
th.mouseReleased = true
|
||||
th.Window = display.NewTermWindow(x, y, w, h, t)
|
||||
th.tab = tab
|
||||
return th
|
||||
}
|
||||
|
||||
@@ -36,6 +38,14 @@ func (t *TermPane) SetID(i uint64) {
|
||||
t.id = i
|
||||
}
|
||||
|
||||
func (t *TermPane) SetTab(tab *Tab) {
|
||||
t.tab = tab
|
||||
}
|
||||
|
||||
func (t *TermPane) Tab() *Tab {
|
||||
return t.tab
|
||||
}
|
||||
|
||||
func (t *TermPane) Close() {}
|
||||
|
||||
func (t *TermPane) Quit() {
|
||||
@@ -80,6 +90,10 @@ func (t *TermPane) HandleEvent(event tcell.Event) {
|
||||
} else if t.Status != shell.TTDone {
|
||||
t.WriteString(event.EscSeq())
|
||||
}
|
||||
} else if _, ok := event.(*tcell.EventPaste); ok {
|
||||
if t.Status != shell.TTDone {
|
||||
t.WriteString(event.EscSeq())
|
||||
}
|
||||
} else if e, ok := event.(*tcell.EventMouse); e != nil && (!ok || t.State.Mode(terminal.ModeMouseMask)) {
|
||||
// t.WriteString(event.EscSeq())
|
||||
} else if e != nil {
|
||||
|
||||
@@ -6,10 +6,12 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
@@ -75,16 +77,24 @@ type SharedBuffer struct {
|
||||
// Whether or not suggestions can be autocompleted must be shared because
|
||||
// it changes based on how the buffer has changed
|
||||
HasSuggestions bool
|
||||
|
||||
// Modifications is the list of modified regions for syntax highlighting
|
||||
Modifications []Loc
|
||||
}
|
||||
|
||||
func (b *SharedBuffer) insert(pos Loc, value []byte) {
|
||||
b.isModified = true
|
||||
b.HasSuggestions = false
|
||||
b.LineArray.insert(pos, value)
|
||||
|
||||
// b.Modifications is cleared every screen redraw so it's
|
||||
// ok to append duplicates
|
||||
b.Modifications = append(b.Modifications, Loc{pos.Y, pos.Y + bytes.Count(value, []byte{'\n'})})
|
||||
}
|
||||
func (b *SharedBuffer) remove(start, end Loc) []byte {
|
||||
b.isModified = true
|
||||
b.HasSuggestions = false
|
||||
b.Modifications = append(b.Modifications, Loc{start.Y, start.Y})
|
||||
return b.LineArray.remove(start, end)
|
||||
}
|
||||
|
||||
@@ -114,9 +124,8 @@ type Buffer struct {
|
||||
// This stores the highlighting rules and filetype detection info
|
||||
SyntaxDef *highlight.Def
|
||||
// The Highlighter struct actually performs the highlighting
|
||||
Highlighter *highlight.Highlighter
|
||||
// Modifications is the list of modified regions for syntax highlighting
|
||||
Modifications []Loc
|
||||
Highlighter *highlight.Highlighter
|
||||
HighlightLock sync.Mutex
|
||||
|
||||
// Hash of the original buffer -- empty if fastdirty is on
|
||||
origHash [md5.Size]byte
|
||||
@@ -331,10 +340,6 @@ func (b *Buffer) Insert(start Loc, text string) {
|
||||
b.EventHandler.active = b.curCursor
|
||||
b.EventHandler.Insert(start, text)
|
||||
|
||||
// b.Modifications is cleared every screen redraw so it's
|
||||
// ok to append duplicates
|
||||
b.Modifications = append(b.Modifications, Loc{start.Y, start.Y + strings.Count(text, "\n")})
|
||||
|
||||
go b.Backup(true)
|
||||
}
|
||||
}
|
||||
@@ -346,8 +351,6 @@ func (b *Buffer) Remove(start, end Loc) {
|
||||
b.EventHandler.active = b.curCursor
|
||||
b.EventHandler.Remove(start, end)
|
||||
|
||||
b.Modifications = append(b.Modifications, Loc{start.Y, start.Y})
|
||||
|
||||
go b.Backup(true)
|
||||
}
|
||||
}
|
||||
@@ -525,6 +528,7 @@ func (b *Buffer) UpdateRules() {
|
||||
if syntaxFile == "" {
|
||||
// search for the syntax file in the user's custom syntax files
|
||||
for _, f := range config.ListRealRuntimeFiles(config.RTSyntax) {
|
||||
log.Println("real runtime file", f.Name())
|
||||
data, err := f.Data()
|
||||
if err != nil {
|
||||
screen.TermMessage("Error loading syntax file " + f.Name() + ": " + err.Error())
|
||||
@@ -538,7 +542,7 @@ func (b *Buffer) UpdateRules() {
|
||||
continue
|
||||
}
|
||||
|
||||
if (ft == "unknown" || ft == "" && highlight.MatchFiletype(header.FtDetect, b.Path, b.lines[0].data)) || header.FileType == ft {
|
||||
if ((ft == "unknown" || ft == "") && highlight.MatchFiletype(header.FtDetect, b.Path, b.lines[0].data)) || header.FileType == ft {
|
||||
syndef, err := highlight.ParseDef(file, header)
|
||||
if err != nil {
|
||||
screen.TermMessage("Error parsing syntax file " + f.Name() + ": " + err.Error())
|
||||
@@ -610,7 +614,9 @@ func (b *Buffer) UpdateRules() {
|
||||
}
|
||||
|
||||
if b.Highlighter == nil || syntaxFile != "" {
|
||||
b.Settings["filetype"] = b.SyntaxDef.FileType
|
||||
if b.SyntaxDef != nil {
|
||||
b.Settings["filetype"] = b.SyntaxDef.FileType
|
||||
}
|
||||
} else {
|
||||
b.SyntaxDef = &highlight.EmptyDef
|
||||
}
|
||||
@@ -618,8 +624,11 @@ func (b *Buffer) UpdateRules() {
|
||||
if b.SyntaxDef != nil {
|
||||
b.Highlighter = highlight.NewHighlighter(b.SyntaxDef)
|
||||
if b.Settings["syntax"].(bool) {
|
||||
b.Highlighter.HighlightStates(b)
|
||||
b.Highlighter.HighlightMatches(b, 0, b.End().Y)
|
||||
go func() {
|
||||
b.Highlighter.HighlightStates(b)
|
||||
b.Highlighter.HighlightMatches(b, 0, b.End().Y)
|
||||
screen.DrawChan <- true
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -900,12 +909,12 @@ func ParseCursorLocation(cursorPositions []string) (Loc, error) {
|
||||
}
|
||||
|
||||
startpos.Y, err = strconv.Atoi(cursorPositions[0])
|
||||
startpos.Y -= 1
|
||||
startpos.Y--
|
||||
if err == nil {
|
||||
if len(cursorPositions) > 1 {
|
||||
startpos.X, err = strconv.Atoi(cursorPositions[1])
|
||||
if startpos.X > 0 {
|
||||
startpos.X -= 1
|
||||
startpos.X--
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -918,6 +927,11 @@ func (b *Buffer) Line(i int) string {
|
||||
return string(b.LineBytes(i))
|
||||
}
|
||||
|
||||
func (b *Buffer) Write(bytes []byte) (n int, err error) {
|
||||
b.EventHandler.InsertBytes(b.End(), bytes)
|
||||
return len(bytes), nil
|
||||
}
|
||||
|
||||
// WriteLog writes a string to the log buffer
|
||||
func WriteLog(s string) {
|
||||
LogBuf.EventHandler.Insert(LogBuf.End(), s)
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
package buffer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
dmp "github.com/sergi/go-diff/diffmatchpatch"
|
||||
"github.com/zyedidia/micro/internal/config"
|
||||
ulua "github.com/zyedidia/micro/internal/lua"
|
||||
"github.com/zyedidia/micro/internal/screen"
|
||||
luar "layeh.com/gopher-luar"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -17,7 +23,7 @@ const (
|
||||
// TextEventReplace represents a replace event
|
||||
TextEventReplace = 0
|
||||
|
||||
undoThreshold = 500 // If two events are less than n milliseconds apart, undo both of them
|
||||
undoThreshold = 1000 // If two events are less than n milliseconds apart, undo both of them
|
||||
)
|
||||
|
||||
// TextEvent holds data for a manipulation on some text that can be undone
|
||||
@@ -107,26 +113,49 @@ func (eh *EventHandler) ApplyDiff(new string) {
|
||||
// Insert creates an insert text event and executes it
|
||||
func (eh *EventHandler) Insert(start Loc, textStr string) {
|
||||
text := []byte(textStr)
|
||||
eh.InsertBytes(start, text)
|
||||
}
|
||||
|
||||
// InsertBytes creates an insert text event and executes it
|
||||
func (eh *EventHandler) InsertBytes(start Loc, text []byte) {
|
||||
e := &TextEvent{
|
||||
C: *eh.cursors[eh.active],
|
||||
EventType: TextEventInsert,
|
||||
Deltas: []Delta{{text, start, Loc{0, 0}}},
|
||||
Time: time.Now(),
|
||||
}
|
||||
// oldl := eh.buf.LinesNum()
|
||||
eh.Execute(e)
|
||||
e.Deltas[0].End = start.MoveLA(utf8.RuneCount(text), eh.buf.LineArray)
|
||||
// linecount := eh.buf.LinesNum() - oldl
|
||||
textcount := utf8.RuneCount(text)
|
||||
lastnl := bytes.LastIndex(text, []byte{'\n'})
|
||||
var endX int
|
||||
var textX int
|
||||
if lastnl >= 0 {
|
||||
endX = utf8.RuneCount(text[lastnl:])
|
||||
textX = endX
|
||||
} else {
|
||||
// endX = start.X + textcount
|
||||
textX = textcount
|
||||
}
|
||||
|
||||
e.Deltas[0].End = start.MoveLA(textcount, eh.buf.LineArray)
|
||||
// e.Deltas[0].End = clamp(Loc{endX, start.Y + linecount}, eh.buf.LineArray)
|
||||
end := e.Deltas[0].End
|
||||
|
||||
for _, c := range eh.cursors {
|
||||
move := func(loc Loc) Loc {
|
||||
log.Println("move", loc)
|
||||
if start.Y != end.Y && loc.GreaterThan(start) {
|
||||
loc.Y += end.Y - start.Y
|
||||
} else if loc.Y == start.Y && loc.GreaterEqual(start) {
|
||||
loc = loc.MoveLA(utf8.RuneCount(text), eh.buf.LineArray)
|
||||
loc.Y += end.Y - start.Y
|
||||
loc.X += textX
|
||||
}
|
||||
return loc
|
||||
}
|
||||
c.Loc = move(c.Loc)
|
||||
c.Relocate()
|
||||
c.CurSelection[0] = move(c.CurSelection[0])
|
||||
c.CurSelection[1] = move(c.CurSelection[1])
|
||||
c.OrigSelection[0] = move(c.OrigSelection[0])
|
||||
@@ -187,16 +216,14 @@ func (eh *EventHandler) Execute(t *TextEvent) {
|
||||
}
|
||||
eh.UndoStack.Push(t)
|
||||
|
||||
// TODO: Call plugins on text events
|
||||
// for pl := range loadedPlugins {
|
||||
// ret, err := Call(pl+".onBeforeTextEvent", t)
|
||||
// if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
|
||||
// screen.TermMessage(err)
|
||||
// }
|
||||
// if val, ok := ret.(lua.LBool); ok && val == lua.LFalse {
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
b, err := config.RunPluginFnBool("onBeforeTextEvent", luar.New(ulua.L, eh.buf), luar.New(ulua.L, t))
|
||||
if err != nil {
|
||||
screen.TermMessage(err)
|
||||
}
|
||||
|
||||
if !b {
|
||||
return
|
||||
}
|
||||
|
||||
ExecuteTextEvent(t, eh.buf)
|
||||
}
|
||||
@@ -209,8 +236,7 @@ func (eh *EventHandler) Undo() {
|
||||
}
|
||||
|
||||
startTime := t.Time.UnixNano() / int64(time.Millisecond)
|
||||
|
||||
eh.UndoOneEvent()
|
||||
endTime := startTime - (startTime % undoThreshold)
|
||||
|
||||
for {
|
||||
t = eh.UndoStack.Peek()
|
||||
@@ -218,10 +244,9 @@ func (eh *EventHandler) Undo() {
|
||||
return
|
||||
}
|
||||
|
||||
if startTime-(t.Time.UnixNano()/int64(time.Millisecond)) > undoThreshold {
|
||||
if t.Time.UnixNano()/int64(time.Millisecond) < endTime {
|
||||
return
|
||||
}
|
||||
startTime = t.Time.UnixNano() / int64(time.Millisecond)
|
||||
|
||||
eh.UndoOneEvent()
|
||||
}
|
||||
@@ -261,8 +286,7 @@ func (eh *EventHandler) Redo() {
|
||||
}
|
||||
|
||||
startTime := t.Time.UnixNano() / int64(time.Millisecond)
|
||||
|
||||
eh.RedoOneEvent()
|
||||
endTime := startTime - (startTime % undoThreshold) + undoThreshold
|
||||
|
||||
for {
|
||||
t = eh.RedoStack.Peek()
|
||||
@@ -270,7 +294,7 @@ func (eh *EventHandler) Redo() {
|
||||
return
|
||||
}
|
||||
|
||||
if (t.Time.UnixNano()/int64(time.Millisecond))-startTime > undoThreshold {
|
||||
if t.Time.UnixNano()/int64(time.Millisecond) > endTime {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package buffer
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"sync"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/zyedidia/micro/pkg/highlight"
|
||||
@@ -38,6 +39,7 @@ type Line struct {
|
||||
state highlight.State
|
||||
match highlight.LineMatch
|
||||
rehighlight bool
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -124,12 +126,22 @@ func NewLineArray(size uint64, endings FileFormat, reader io.Reader) *LineArray
|
||||
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
la.lines = Append(la.lines, Line{data[:], nil, nil, false})
|
||||
la.lines = Append(la.lines, Line{
|
||||
data: data[:],
|
||||
state: nil,
|
||||
match: nil,
|
||||
rehighlight: false,
|
||||
})
|
||||
}
|
||||
// Last line was read
|
||||
break
|
||||
} else {
|
||||
la.lines = Append(la.lines, Line{data[:dlen-1], nil, nil, false})
|
||||
la.lines = Append(la.lines, Line{
|
||||
data: data[:dlen-1],
|
||||
state: nil,
|
||||
match: nil,
|
||||
rehighlight: false,
|
||||
})
|
||||
}
|
||||
n++
|
||||
}
|
||||
@@ -155,9 +167,19 @@ func (la *LineArray) Bytes() []byte {
|
||||
|
||||
// newlineBelow adds a newline below the given line number
|
||||
func (la *LineArray) newlineBelow(y int) {
|
||||
la.lines = append(la.lines, Line{[]byte{' '}, nil, nil, false})
|
||||
la.lines = append(la.lines, Line{
|
||||
data: []byte{' '},
|
||||
state: nil,
|
||||
match: nil,
|
||||
rehighlight: false,
|
||||
})
|
||||
copy(la.lines[y+2:], la.lines[y+1:])
|
||||
la.lines[y+1] = Line{[]byte{}, la.lines[y].state, nil, false}
|
||||
la.lines[y+1] = Line{
|
||||
data: []byte{},
|
||||
state: la.lines[y].state,
|
||||
match: nil,
|
||||
rehighlight: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Inserts a byte array at a given location
|
||||
@@ -285,28 +307,40 @@ func (la *LineArray) LineBytes(n int) []byte {
|
||||
|
||||
// State gets the highlight state for the given line number
|
||||
func (la *LineArray) State(lineN int) highlight.State {
|
||||
la.lines[lineN].lock.Lock()
|
||||
defer la.lines[lineN].lock.Unlock()
|
||||
return la.lines[lineN].state
|
||||
}
|
||||
|
||||
// SetState sets the highlight state at the given line number
|
||||
func (la *LineArray) SetState(lineN int, s highlight.State) {
|
||||
la.lines[lineN].lock.Lock()
|
||||
defer la.lines[lineN].lock.Unlock()
|
||||
la.lines[lineN].state = s
|
||||
}
|
||||
|
||||
// SetMatch sets the match at the given line number
|
||||
func (la *LineArray) SetMatch(lineN int, m highlight.LineMatch) {
|
||||
la.lines[lineN].lock.Lock()
|
||||
defer la.lines[lineN].lock.Unlock()
|
||||
la.lines[lineN].match = m
|
||||
}
|
||||
|
||||
// Match retrieves the match for the given line number
|
||||
func (la *LineArray) Match(lineN int) highlight.LineMatch {
|
||||
la.lines[lineN].lock.Lock()
|
||||
defer la.lines[lineN].lock.Unlock()
|
||||
return la.lines[lineN].match
|
||||
}
|
||||
|
||||
func (la *LineArray) Rehighlight(lineN int) bool {
|
||||
la.lines[lineN].lock.Lock()
|
||||
defer la.lines[lineN].lock.Unlock()
|
||||
return la.lines[lineN].rehighlight
|
||||
}
|
||||
|
||||
func (la *LineArray) SetRehighlight(lineN int, on bool) {
|
||||
la.lines[lineN].lock.Lock()
|
||||
defer la.lines[lineN].lock.Unlock()
|
||||
la.lines[lineN].rehighlight = on
|
||||
}
|
||||
|
||||
@@ -135,3 +135,13 @@ func ByteOffset(pos Loc, buf *Buffer) int {
|
||||
loc += len(buf.Line(y)[:x])
|
||||
return loc
|
||||
}
|
||||
|
||||
// clamps a loc within a buffer
|
||||
func clamp(pos Loc, la *LineArray) Loc {
|
||||
if pos.GreaterEqual(la.End()) {
|
||||
return la.End().MoveLA(-1, la)
|
||||
} else if pos.LessThan(la.Start()) {
|
||||
return la.Start()
|
||||
}
|
||||
return pos
|
||||
}
|
||||
|
||||
@@ -13,13 +13,19 @@ const (
|
||||
MTError
|
||||
)
|
||||
|
||||
// Message represents the information for a gutter message
|
||||
type Message struct {
|
||||
Msg string
|
||||
// The Msg iteslf
|
||||
Msg string
|
||||
// Start and End locations for the message
|
||||
Start, End Loc
|
||||
Kind MsgType
|
||||
Owner string
|
||||
// The Kind stores the message type
|
||||
Kind MsgType
|
||||
// The Owner of the message
|
||||
Owner string
|
||||
}
|
||||
|
||||
// NewMessage creates a new gutter message
|
||||
func NewMessage(owner string, msg string, start, end Loc, kind MsgType) *Message {
|
||||
return &Message{
|
||||
Msg: msg,
|
||||
@@ -30,6 +36,7 @@ func NewMessage(owner string, msg string, start, end Loc, kind MsgType) *Message
|
||||
}
|
||||
}
|
||||
|
||||
// NewMessageAtLine creates a new gutter message at a given line
|
||||
func NewMessageAtLine(owner string, msg string, line int, kind MsgType) *Message {
|
||||
start := Loc{-1, line - 1}
|
||||
end := start
|
||||
|
||||
@@ -28,41 +28,41 @@ const LargeFileThreshold = 50000
|
||||
// the supplied function with the file as io.Writer object, also making sure the file is
|
||||
// closed afterwards.
|
||||
func overwriteFile(name string, enc encoding.Encoding, fn func(io.Writer) error, withSudo bool) (err error) {
|
||||
var writeCloser io.WriteCloser
|
||||
var writeCloser io.WriteCloser
|
||||
|
||||
if withSudo {
|
||||
cmd := exec.Command(config.GlobalSettings["sucmd"].(string), "dd", "bs=4k", "of="+name)
|
||||
if withSudo {
|
||||
cmd := exec.Command(config.GlobalSettings["sucmd"].(string), "dd", "bs=4k", "of="+name)
|
||||
|
||||
if writeCloser, err = cmd.StdinPipe(); err != nil {
|
||||
return
|
||||
}
|
||||
if writeCloser, err = cmd.StdinPipe(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
<-c
|
||||
cmd.Process.Kill()
|
||||
}()
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
<-c
|
||||
cmd.Process.Kill()
|
||||
}()
|
||||
|
||||
defer func() {
|
||||
screenb := screen.TempFini()
|
||||
if e := cmd.Run(); e != nil && err == nil {
|
||||
err = e
|
||||
}
|
||||
screen.TempStart(screenb)
|
||||
}()
|
||||
} else if writeCloser, err = os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
screenb := screen.TempFini()
|
||||
if e := cmd.Run(); e != nil && err == nil {
|
||||
err = e
|
||||
}
|
||||
screen.TempStart(screenb)
|
||||
}()
|
||||
} else if writeCloser, err = os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
w := transform.NewWriter(writeCloser, enc.NewEncoder())
|
||||
err = fn(w)
|
||||
w := transform.NewWriter(writeCloser, enc.NewEncoder())
|
||||
err = fn(w)
|
||||
|
||||
if e := writeCloser.Close(); e != nil && err == nil {
|
||||
err = e
|
||||
}
|
||||
if e := writeCloser.Close(); e != nil && err == nil {
|
||||
err = e
|
||||
}
|
||||
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
// Save saves the buffer to its default path
|
||||
@@ -92,7 +92,7 @@ func (b *Buffer) saveToFile(filename string, withSudo bool) error {
|
||||
return errors.New("Cannot save scratch buffer")
|
||||
}
|
||||
if withSudo && runtime.GOOS == "windows" {
|
||||
return errors.New("Save with sudo not supported on Windows")
|
||||
return errors.New("Save with sudo not supported on Windows")
|
||||
}
|
||||
|
||||
b.UpdateRules()
|
||||
@@ -178,7 +178,7 @@ func (b *Buffer) saveToFile(filename string, withSudo bool) error {
|
||||
}
|
||||
|
||||
if err = overwriteFile(absFilename, enc, fwriter, withSudo); err != nil {
|
||||
return err
|
||||
return err
|
||||
}
|
||||
|
||||
if !b.Settings["fastdirty"].(bool) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/encoding"
|
||||
@@ -49,7 +50,7 @@ func (b *Buffer) Unserialize() error {
|
||||
if b.Path == "" {
|
||||
return nil
|
||||
}
|
||||
file, err := os.Open(config.ConfigDir + "/buffers/" + util.EscapePath(b.AbsPath))
|
||||
file, err := os.Open(filepath.Join(config.ConfigDir, "buffers", util.EscapePath(b.AbsPath)))
|
||||
defer file.Close()
|
||||
if err == nil {
|
||||
var buffer SerializedBuffer
|
||||
|
||||
@@ -35,7 +35,7 @@ func (b *Buffer) SetOptionNative(option string, nativeValue interface{}) error {
|
||||
}
|
||||
} else if option == "encoding" {
|
||||
b.isModified = true
|
||||
} else if option == "readonly" && b.Type == BTDefault {
|
||||
} else if option == "readonly" && b.Type.Kind == BTDefault.Kind {
|
||||
b.Type.Readonly = nativeValue.(bool)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@ package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
lua "github.com/yuin/gopher-lua"
|
||||
ulua "github.com/zyedidia/micro/internal/lua"
|
||||
)
|
||||
|
||||
// ErrNoSuchFunction is returned when Call is executed on a function that does not exist
|
||||
var ErrNoSuchFunction = errors.New("No such function exists")
|
||||
|
||||
// LoadAllPlugins loads all detected plugins (in runtime/plugins and ConfigDir/plugins)
|
||||
@@ -55,15 +57,14 @@ func RunPluginFnBool(fn string, args ...lua.LValue) (bool, error) {
|
||||
reterr = errors.New("Plugin " + p.Name + ": " + err.Error())
|
||||
continue
|
||||
}
|
||||
if v, ok := val.(lua.LBool); !ok {
|
||||
reterr = errors.New(p.Name + "." + fn + " should return a boolean")
|
||||
} else {
|
||||
if v, ok := val.(lua.LBool); ok {
|
||||
retbool = retbool && bool(v)
|
||||
}
|
||||
}
|
||||
return retbool, reterr
|
||||
}
|
||||
|
||||
// Plugin stores information about the source files/info for a plugin
|
||||
type Plugin struct {
|
||||
DirName string // name of plugin folder
|
||||
Name string // name of plugin
|
||||
@@ -73,6 +74,7 @@ type Plugin struct {
|
||||
Default bool // pre-installed plugin
|
||||
}
|
||||
|
||||
// IsEnabled returns if a plugin is enabled
|
||||
func (p *Plugin) IsEnabled() bool {
|
||||
if v, ok := GlobalSettings[p.Name]; ok {
|
||||
return v.(bool) && p.Loaded
|
||||
@@ -80,13 +82,15 @@ func (p *Plugin) IsEnabled() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Plugins is a list of all detected plugins (enabled or disabled)
|
||||
var Plugins []*Plugin
|
||||
|
||||
// Load creates an option for the plugin and runs all source files
|
||||
func (p *Plugin) Load() error {
|
||||
if v, ok := GlobalSettings[p.Name]; ok && !v.(bool) {
|
||||
return nil
|
||||
}
|
||||
for _, f := range p.Srcs {
|
||||
if v, ok := GlobalSettings[p.Name]; ok && !v.(bool) {
|
||||
return nil
|
||||
}
|
||||
dat, err := f.Data()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -95,14 +99,19 @@ func (p *Plugin) Load() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.Loaded = true
|
||||
RegisterGlobalOption(p.Name, true)
|
||||
}
|
||||
p.Loaded = true
|
||||
RegisterGlobalOption(p.Name, true)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Call calls a given function in this plugin
|
||||
func (p *Plugin) Call(fn string, args ...lua.LValue) (lua.LValue, error) {
|
||||
plug := ulua.L.GetGlobal(p.Name)
|
||||
if plug == lua.LNil {
|
||||
log.Println("Plugin does not exist:", p.Name, "at", p.DirName, ":", p)
|
||||
return nil, nil
|
||||
}
|
||||
luafn := ulua.L.GetField(plug, fn)
|
||||
if luafn == lua.LNil {
|
||||
return nil, ErrNoSuchFunction
|
||||
@@ -120,7 +129,23 @@ func (p *Plugin) Call(fn string, args ...lua.LValue) (lua.LValue, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// FindPlugin returns the plugin with the given name
|
||||
func FindPlugin(name string) *Plugin {
|
||||
var pl *Plugin
|
||||
for _, p := range Plugins {
|
||||
if !p.IsEnabled() {
|
||||
continue
|
||||
}
|
||||
if p.Name == name {
|
||||
pl = p
|
||||
break
|
||||
}
|
||||
}
|
||||
return pl
|
||||
}
|
||||
|
||||
// FindAnyPlugin does not require the plugin to be enabled
|
||||
func FindAnyPlugin(name string) *Plugin {
|
||||
var pl *Plugin
|
||||
for _, p := range Plugins {
|
||||
if p.Name == name {
|
||||
|
||||
712
internal/config/plugin_installer.go
Normal file
712
internal/config/plugin_installer.go
Normal file
@@ -0,0 +1,712 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/blang/semver"
|
||||
lua "github.com/yuin/gopher-lua"
|
||||
"github.com/zyedidia/json5"
|
||||
ulua "github.com/zyedidia/micro/internal/lua"
|
||||
"github.com/zyedidia/micro/internal/util"
|
||||
)
|
||||
|
||||
var (
|
||||
allPluginPackages PluginPackages
|
||||
)
|
||||
|
||||
// CorePluginName is a plugin dependency name for the micro core.
|
||||
const CorePluginName = "micro"
|
||||
|
||||
// PluginChannel contains an url to a json list of PluginRepository
|
||||
type PluginChannel string
|
||||
|
||||
// PluginChannels is a slice of PluginChannel
|
||||
type PluginChannels []PluginChannel
|
||||
|
||||
// PluginRepository contains an url to json file containing PluginPackages
|
||||
type PluginRepository string
|
||||
|
||||
// PluginPackage contains the meta-data of a plugin and all available versions
|
||||
type PluginPackage struct {
|
||||
Name string
|
||||
Description string
|
||||
Author string
|
||||
Tags []string
|
||||
Versions PluginVersions
|
||||
}
|
||||
|
||||
// PluginPackages is a list of PluginPackage instances.
|
||||
type PluginPackages []*PluginPackage
|
||||
|
||||
// PluginVersion descripes a version of a PluginPackage. Containing a version, download url and also dependencies.
|
||||
type PluginVersion struct {
|
||||
pack *PluginPackage
|
||||
Version semver.Version
|
||||
Url string
|
||||
Require PluginDependencies
|
||||
}
|
||||
|
||||
func (pv *PluginVersion) Pack() *PluginPackage {
|
||||
return pv.pack
|
||||
}
|
||||
|
||||
// PluginVersions is a slice of PluginVersion
|
||||
type PluginVersions []*PluginVersion
|
||||
|
||||
// PluginDependency descripes a dependency to another plugin or micro itself.
|
||||
type PluginDependency struct {
|
||||
Name string
|
||||
Range semver.Range
|
||||
}
|
||||
|
||||
// PluginDependencies is a slice of PluginDependency
|
||||
type PluginDependencies []*PluginDependency
|
||||
|
||||
func (pp *PluginPackage) String() string {
|
||||
buf := new(bytes.Buffer)
|
||||
buf.WriteString("Plugin: ")
|
||||
buf.WriteString(pp.Name)
|
||||
buf.WriteRune('\n')
|
||||
if pp.Author != "" {
|
||||
buf.WriteString("Author: ")
|
||||
buf.WriteString(pp.Author)
|
||||
buf.WriteRune('\n')
|
||||
}
|
||||
if pp.Description != "" {
|
||||
buf.WriteRune('\n')
|
||||
buf.WriteString(pp.Description)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func fetchAllSources(count int, fetcher func(i int) PluginPackages) PluginPackages {
|
||||
wgQuery := new(sync.WaitGroup)
|
||||
wgQuery.Add(count)
|
||||
|
||||
results := make(chan PluginPackages)
|
||||
|
||||
wgDone := new(sync.WaitGroup)
|
||||
wgDone.Add(1)
|
||||
var packages PluginPackages
|
||||
for i := 0; i < count; i++ {
|
||||
go func(i int) {
|
||||
results <- fetcher(i)
|
||||
wgQuery.Done()
|
||||
}(i)
|
||||
}
|
||||
go func() {
|
||||
packages = make(PluginPackages, 0)
|
||||
for res := range results {
|
||||
packages = append(packages, res...)
|
||||
}
|
||||
wgDone.Done()
|
||||
}()
|
||||
wgQuery.Wait()
|
||||
close(results)
|
||||
wgDone.Wait()
|
||||
return packages
|
||||
}
|
||||
|
||||
// Fetch retrieves all available PluginPackages from the given channels
|
||||
func (pc PluginChannels) Fetch(out io.Writer) PluginPackages {
|
||||
return fetchAllSources(len(pc), func(i int) PluginPackages {
|
||||
return pc[i].Fetch(out)
|
||||
})
|
||||
}
|
||||
|
||||
// Fetch retrieves all available PluginPackages from the given channel
|
||||
func (pc PluginChannel) Fetch(out io.Writer) PluginPackages {
|
||||
resp, err := http.Get(string(pc))
|
||||
if err != nil {
|
||||
fmt.Fprintln(out, "Failed to query plugin channel:\n", err)
|
||||
return PluginPackages{}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
decoder := json5.NewDecoder(resp.Body)
|
||||
|
||||
var repositories []PluginRepository
|
||||
if err := decoder.Decode(&repositories); err != nil {
|
||||
fmt.Fprintln(out, "Failed to decode channel data:\n", err)
|
||||
return PluginPackages{}
|
||||
}
|
||||
return fetchAllSources(len(repositories), func(i int) PluginPackages {
|
||||
return repositories[i].Fetch(out)
|
||||
})
|
||||
}
|
||||
|
||||
// Fetch retrieves all available PluginPackages from the given repository
|
||||
func (pr PluginRepository) Fetch(out io.Writer) PluginPackages {
|
||||
resp, err := http.Get(string(pr))
|
||||
if err != nil {
|
||||
fmt.Fprintln(out, "Failed to query plugin repository:\n", err)
|
||||
return PluginPackages{}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
decoder := json5.NewDecoder(resp.Body)
|
||||
|
||||
var plugins PluginPackages
|
||||
if err := decoder.Decode(&plugins); err != nil {
|
||||
fmt.Fprintln(out, "Failed to decode repository data:\n", err)
|
||||
return PluginPackages{}
|
||||
}
|
||||
if len(plugins) > 0 {
|
||||
return PluginPackages{plugins[0]}
|
||||
}
|
||||
return nil
|
||||
// return plugins
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals raw json to a PluginVersion
|
||||
func (pv *PluginVersion) UnmarshalJSON(data []byte) error {
|
||||
var values struct {
|
||||
Version semver.Version
|
||||
Url string
|
||||
Require map[string]string
|
||||
}
|
||||
|
||||
if err := json5.Unmarshal(data, &values); err != nil {
|
||||
return err
|
||||
}
|
||||
pv.Version = values.Version
|
||||
pv.Url = values.Url
|
||||
pv.Require = make(PluginDependencies, 0)
|
||||
|
||||
for k, v := range values.Require {
|
||||
// don't add the dependency if it's the core and
|
||||
// we have a unknown version number.
|
||||
// in that case just accept that dependency (which equals to not adding it.)
|
||||
if k != CorePluginName || !isUnknownCoreVersion() {
|
||||
if vRange, err := semver.ParseRange(v); err == nil {
|
||||
pv.Require = append(pv.Require, &PluginDependency{k, vRange})
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals raw json to a PluginPackage
|
||||
func (pp *PluginPackage) UnmarshalJSON(data []byte) error {
|
||||
var values struct {
|
||||
Name string
|
||||
Description string
|
||||
Author string
|
||||
Tags []string
|
||||
Versions PluginVersions
|
||||
}
|
||||
if err := json5.Unmarshal(data, &values); err != nil {
|
||||
return err
|
||||
}
|
||||
pp.Name = values.Name
|
||||
pp.Description = values.Description
|
||||
pp.Author = values.Author
|
||||
pp.Tags = values.Tags
|
||||
pp.Versions = values.Versions
|
||||
for _, v := range pp.Versions {
|
||||
v.pack = pp
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAllPluginPackages gets all PluginPackages which may be available.
|
||||
func GetAllPluginPackages(out io.Writer) PluginPackages {
|
||||
if allPluginPackages == nil {
|
||||
getOption := func(name string) []string {
|
||||
data := GetGlobalOption(name)
|
||||
if strs, ok := data.([]string); ok {
|
||||
return strs
|
||||
}
|
||||
if ifs, ok := data.([]interface{}); ok {
|
||||
result := make([]string, len(ifs))
|
||||
for i, urlIf := range ifs {
|
||||
if url, ok := urlIf.(string); ok {
|
||||
result[i] = url
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
channels := PluginChannels{}
|
||||
for _, url := range getOption("pluginchannels") {
|
||||
channels = append(channels, PluginChannel(url))
|
||||
}
|
||||
repos := []PluginRepository{}
|
||||
for _, url := range getOption("pluginrepos") {
|
||||
repos = append(repos, PluginRepository(url))
|
||||
}
|
||||
allPluginPackages = fetchAllSources(len(repos)+1, func(i int) PluginPackages {
|
||||
if i == 0 {
|
||||
return channels.Fetch(out)
|
||||
}
|
||||
return repos[i-1].Fetch(out)
|
||||
})
|
||||
}
|
||||
return allPluginPackages
|
||||
}
|
||||
|
||||
func (pv PluginVersions) find(ppName string) *PluginVersion {
|
||||
for _, v := range pv {
|
||||
if v.pack.Name == ppName {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Len returns the number of pluginversions in this slice
|
||||
func (pv PluginVersions) Len() int {
|
||||
return len(pv)
|
||||
}
|
||||
|
||||
// Swap two entries of the slice
|
||||
func (pv PluginVersions) Swap(i, j int) {
|
||||
pv[i], pv[j] = pv[j], pv[i]
|
||||
}
|
||||
|
||||
// Less returns true if the version at position i is greater then the version at position j (used for sorting)
|
||||
func (pv PluginVersions) Less(i, j int) bool {
|
||||
return pv[i].Version.GT(pv[j].Version)
|
||||
}
|
||||
|
||||
// Match returns true if the package matches a given search text
|
||||
func (pp PluginPackage) Match(text string) bool {
|
||||
text = strings.ToLower(text)
|
||||
for _, t := range pp.Tags {
|
||||
if strings.ToLower(t) == text {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if strings.Contains(strings.ToLower(pp.Name), text) {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.Contains(strings.ToLower(pp.Description), text) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// IsInstallable returns true if the package can be installed.
|
||||
func (pp PluginPackage) IsInstallable(out io.Writer) error {
|
||||
_, err := GetAllPluginPackages(out).Resolve(GetInstalledVersions(true), PluginDependencies{
|
||||
&PluginDependency{
|
||||
Name: pp.Name,
|
||||
Range: semver.Range(func(v semver.Version) bool { return true }),
|
||||
}})
|
||||
return err
|
||||
}
|
||||
|
||||
// SearchPlugin retrieves a list of all PluginPackages which match the given search text and
|
||||
// could be or are already installed
|
||||
func SearchPlugin(out io.Writer, texts []string) (plugins PluginPackages) {
|
||||
plugins = make(PluginPackages, 0)
|
||||
|
||||
pluginLoop:
|
||||
for _, pp := range GetAllPluginPackages(out) {
|
||||
for _, text := range texts {
|
||||
if !pp.Match(text) {
|
||||
continue pluginLoop
|
||||
}
|
||||
}
|
||||
|
||||
if err := pp.IsInstallable(out); err == nil {
|
||||
plugins = append(plugins, pp)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func isUnknownCoreVersion() bool {
|
||||
_, err := semver.ParseTolerant(util.Version)
|
||||
return err != nil
|
||||
}
|
||||
|
||||
func newStaticPluginVersion(name, version string) *PluginVersion {
|
||||
vers, err := semver.ParseTolerant(version)
|
||||
|
||||
if err != nil {
|
||||
if vers, err = semver.ParseTolerant("0.0.0-" + version); err != nil {
|
||||
vers = semver.MustParse("0.0.0-unknown")
|
||||
}
|
||||
}
|
||||
pl := &PluginPackage{
|
||||
Name: name,
|
||||
}
|
||||
pv := &PluginVersion{
|
||||
pack: pl,
|
||||
Version: vers,
|
||||
}
|
||||
pl.Versions = PluginVersions{pv}
|
||||
return pv
|
||||
}
|
||||
|
||||
// GetInstalledVersions returns a list of all currently installed plugins including an entry for
|
||||
// micro itself. This can be used to resolve dependencies.
|
||||
func GetInstalledVersions(withCore bool) PluginVersions {
|
||||
result := PluginVersions{}
|
||||
if withCore {
|
||||
result = append(result, newStaticPluginVersion(CorePluginName, util.Version))
|
||||
}
|
||||
|
||||
for _, p := range Plugins {
|
||||
if !p.IsEnabled() {
|
||||
continue
|
||||
}
|
||||
version := GetInstalledPluginVersion(p.Name)
|
||||
if pv := newStaticPluginVersion(p.Name, version); pv != nil {
|
||||
result = append(result, pv)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// GetInstalledPluginVersion returns the string of the exported VERSION variable of a loaded plugin
|
||||
func GetInstalledPluginVersion(name string) string {
|
||||
plugin := ulua.L.GetGlobal(name)
|
||||
if plugin != lua.LNil {
|
||||
version := ulua.L.GetField(plugin, "VERSION")
|
||||
if str, ok := version.(lua.LString); ok {
|
||||
return string(str)
|
||||
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// DownloadAndInstall downloads and installs the given plugin and version
|
||||
func (pv *PluginVersion) DownloadAndInstall(out io.Writer) error {
|
||||
fmt.Fprintf(out, "Downloading %q (%s) from %q\n", pv.pack.Name, pv.Version, pv.Url)
|
||||
resp, err := http.Get(pv.Url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
zipbuf := bytes.NewReader(data)
|
||||
z, err := zip.NewReader(zipbuf, zipbuf.Size())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
targetDir := filepath.Join(ConfigDir, "plug", pv.pack.Name)
|
||||
dirPerm := os.FileMode(0755)
|
||||
if err = os.MkdirAll(targetDir, dirPerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if all files in zip are in the same directory.
|
||||
// this might be the case if the plugin zip contains the whole plugin dir
|
||||
// instead of its content.
|
||||
var prefix string
|
||||
allPrefixed := false
|
||||
for i, f := range z.File {
|
||||
parts := strings.Split(f.Name, "/")
|
||||
if i == 0 {
|
||||
prefix = parts[0]
|
||||
} else if parts[0] != prefix {
|
||||
allPrefixed = false
|
||||
break
|
||||
} else {
|
||||
// switch to true since we have at least a second file
|
||||
allPrefixed = true
|
||||
}
|
||||
}
|
||||
|
||||
// Install files and directory's
|
||||
for _, f := range z.File {
|
||||
parts := strings.Split(f.Name, "/")
|
||||
if allPrefixed {
|
||||
parts = parts[1:]
|
||||
}
|
||||
|
||||
targetName := filepath.Join(targetDir, filepath.Join(parts...))
|
||||
if f.FileInfo().IsDir() {
|
||||
if err := os.MkdirAll(targetName, dirPerm); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
basepath := filepath.Dir(targetName)
|
||||
|
||||
if err := os.MkdirAll(basepath, dirPerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
content, err := f.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer content.Close()
|
||||
target, err := os.Create(targetName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer target.Close()
|
||||
if _, err = io.Copy(target, content); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pl PluginPackages) Get(name string) *PluginPackage {
|
||||
for _, p := range pl {
|
||||
if p.Name == name {
|
||||
return p
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pl PluginPackages) GetAllVersions(name string) PluginVersions {
|
||||
result := make(PluginVersions, 0)
|
||||
p := pl.Get(name)
|
||||
if p != nil {
|
||||
for _, v := range p.Versions {
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (req PluginDependencies) Join(other PluginDependencies) PluginDependencies {
|
||||
m := make(map[string]*PluginDependency)
|
||||
for _, r := range req {
|
||||
m[r.Name] = r
|
||||
}
|
||||
for _, o := range other {
|
||||
cur, ok := m[o.Name]
|
||||
if ok {
|
||||
m[o.Name] = &PluginDependency{
|
||||
o.Name,
|
||||
o.Range.AND(cur.Range),
|
||||
}
|
||||
} else {
|
||||
m[o.Name] = o
|
||||
}
|
||||
}
|
||||
result := make(PluginDependencies, 0, len(m))
|
||||
for _, v := range m {
|
||||
result = append(result, v)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Resolve resolves dependencies between different plugins
|
||||
func (all PluginPackages) Resolve(selectedVersions PluginVersions, open PluginDependencies) (PluginVersions, error) {
|
||||
if len(open) == 0 {
|
||||
return selectedVersions, nil
|
||||
}
|
||||
currentRequirement, stillOpen := open[0], open[1:]
|
||||
if currentRequirement != nil {
|
||||
if selVersion := selectedVersions.find(currentRequirement.Name); selVersion != nil {
|
||||
if currentRequirement.Range(selVersion.Version) {
|
||||
return all.Resolve(selectedVersions, stillOpen)
|
||||
}
|
||||
return nil, fmt.Errorf("unable to find a matching version for \"%s\"", currentRequirement.Name)
|
||||
}
|
||||
availableVersions := all.GetAllVersions(currentRequirement.Name)
|
||||
sort.Sort(availableVersions)
|
||||
|
||||
for _, version := range availableVersions {
|
||||
if currentRequirement.Range(version.Version) {
|
||||
resolved, err := all.Resolve(append(selectedVersions, version), stillOpen.Join(version.Require))
|
||||
|
||||
if err == nil {
|
||||
return resolved, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("unable to find a matching version for \"%s\"", currentRequirement.Name)
|
||||
}
|
||||
return selectedVersions, nil
|
||||
}
|
||||
|
||||
func (pv PluginVersions) install(out io.Writer) {
|
||||
anyInstalled := false
|
||||
currentlyInstalled := GetInstalledVersions(true)
|
||||
|
||||
for _, sel := range pv {
|
||||
if sel.pack.Name != CorePluginName {
|
||||
shouldInstall := true
|
||||
if pv := currentlyInstalled.find(sel.pack.Name); pv != nil {
|
||||
if pv.Version.NE(sel.Version) {
|
||||
fmt.Fprintln(out, "Uninstalling", sel.pack.Name)
|
||||
UninstallPlugin(out, sel.pack.Name)
|
||||
} else {
|
||||
shouldInstall = false
|
||||
}
|
||||
}
|
||||
|
||||
if shouldInstall {
|
||||
if err := sel.DownloadAndInstall(out); err != nil {
|
||||
fmt.Fprintln(out, err)
|
||||
return
|
||||
}
|
||||
anyInstalled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if anyInstalled {
|
||||
fmt.Fprintln(out, "One or more plugins installed.")
|
||||
} else {
|
||||
fmt.Fprintln(out, "Nothing to install / update")
|
||||
}
|
||||
}
|
||||
|
||||
// UninstallPlugin deletes the plugin folder of the given plugin
|
||||
func UninstallPlugin(out io.Writer, name string) {
|
||||
for _, p := range Plugins {
|
||||
if !p.IsEnabled() {
|
||||
continue
|
||||
}
|
||||
if p.Name == name {
|
||||
p.Loaded = false
|
||||
if err := os.RemoveAll(filepath.Join(ConfigDir, "plug", p.DirName)); err != nil {
|
||||
fmt.Fprintln(out, err)
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Install installs the plugin
|
||||
func (pl PluginPackage) Install(out io.Writer) {
|
||||
selected, err := GetAllPluginPackages(out).Resolve(GetInstalledVersions(true), PluginDependencies{
|
||||
&PluginDependency{
|
||||
Name: pl.Name,
|
||||
Range: semver.Range(func(v semver.Version) bool { return true }),
|
||||
}})
|
||||
if err != nil {
|
||||
fmt.Fprintln(out, err)
|
||||
return
|
||||
}
|
||||
selected.install(out)
|
||||
}
|
||||
|
||||
// UpdatePlugins updates the given plugins
|
||||
func UpdatePlugins(out io.Writer, plugins []string) {
|
||||
// if no plugins are specified, update all installed plugins.
|
||||
if len(plugins) == 0 {
|
||||
for _, p := range Plugins {
|
||||
if !p.IsEnabled() {
|
||||
continue
|
||||
}
|
||||
plugins = append(plugins, p.Name)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintln(out, "Checking for plugin updates")
|
||||
microVersion := PluginVersions{
|
||||
newStaticPluginVersion(CorePluginName, util.Version),
|
||||
}
|
||||
|
||||
var updates = make(PluginDependencies, 0)
|
||||
for _, name := range plugins {
|
||||
pv := GetInstalledPluginVersion(name)
|
||||
r, err := semver.ParseRange(">=" + pv) // Try to get newer versions.
|
||||
if err == nil {
|
||||
updates = append(updates, &PluginDependency{
|
||||
Name: name,
|
||||
Range: r,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
selected, err := GetAllPluginPackages(out).Resolve(microVersion, updates)
|
||||
if err != nil {
|
||||
fmt.Fprintln(out, err)
|
||||
return
|
||||
}
|
||||
selected.install(out)
|
||||
}
|
||||
|
||||
func PluginCommand(out io.Writer, cmd string, args []string) {
|
||||
switch cmd {
|
||||
case "install":
|
||||
installedVersions := GetInstalledVersions(false)
|
||||
for _, plugin := range args {
|
||||
pp := GetAllPluginPackages(out).Get(plugin)
|
||||
if pp == nil {
|
||||
fmt.Fprintln(out, "Unknown plugin \""+plugin+"\"")
|
||||
} else if err := pp.IsInstallable(out); err != nil {
|
||||
fmt.Fprintln(out, "Error installing ", plugin, ": ", err)
|
||||
} else {
|
||||
for _, installed := range installedVersions {
|
||||
if pp.Name == installed.Pack().Name {
|
||||
if pp.Versions[0].Version.Compare(installed.Version) == 1 {
|
||||
fmt.Fprintln(out, pp.Name, " is already installed but out-of-date: use 'plugin update ", pp.Name, "' to update")
|
||||
} else {
|
||||
fmt.Fprintln(out, pp.Name, " is already installed")
|
||||
}
|
||||
}
|
||||
}
|
||||
pp.Install(out)
|
||||
}
|
||||
}
|
||||
|
||||
case "remove":
|
||||
removed := ""
|
||||
for _, plugin := range args {
|
||||
// check if the plugin exists.
|
||||
for _, p := range Plugins {
|
||||
if p.Name == plugin && p.Default {
|
||||
fmt.Fprintln(out, "Default plugins cannot be removed, but can be disabled via settings.")
|
||||
continue
|
||||
}
|
||||
if p.Name == plugin {
|
||||
UninstallPlugin(out, plugin)
|
||||
removed += plugin + " "
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if removed != "" {
|
||||
fmt.Fprintln(out, "Removed ", removed)
|
||||
} else {
|
||||
fmt.Fprintln(out, "No plugins removed")
|
||||
}
|
||||
case "update":
|
||||
UpdatePlugins(out, args)
|
||||
case "list":
|
||||
plugins := GetInstalledVersions(false)
|
||||
fmt.Fprintln(out, "The following plugins are currently installed:")
|
||||
for _, p := range plugins {
|
||||
fmt.Fprintf(out, "%s (%s)\n", p.Pack().Name, p.Version)
|
||||
}
|
||||
case "search":
|
||||
plugins := SearchPlugin(out, args)
|
||||
fmt.Fprintln(out, len(plugins), " plugins found")
|
||||
for _, p := range plugins {
|
||||
fmt.Fprintln(out, "----------------")
|
||||
fmt.Fprintln(out, p.String())
|
||||
}
|
||||
fmt.Fprintln(out, "----------------")
|
||||
case "available":
|
||||
packages := GetAllPluginPackages(out)
|
||||
fmt.Fprintln(out, "Available Plugins:")
|
||||
for _, pkg := range packages {
|
||||
fmt.Fprintln(out, pkg.Name)
|
||||
}
|
||||
default:
|
||||
fmt.Fprintln(out, "Invalid plugin command")
|
||||
}
|
||||
}
|
||||
56
internal/config/plugin_installer_test.go
Normal file
56
internal/config/plugin_installer_test.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/blang/semver"
|
||||
|
||||
"github.com/zyedidia/json5"
|
||||
)
|
||||
|
||||
func TestDependencyResolving(t *testing.T) {
|
||||
js := `
|
||||
[{
|
||||
"Name": "Foo",
|
||||
"Versions": [{ "Version": "1.0.0" }, { "Version": "1.5.0" },{ "Version": "2.0.0" }]
|
||||
}, {
|
||||
"Name": "Bar",
|
||||
"Versions": [{ "Version": "1.0.0", "Require": {"Foo": ">1.0.0 <2.0.0"} }]
|
||||
}, {
|
||||
"Name": "Unresolvable",
|
||||
"Versions": [{ "Version": "1.0.0", "Require": {"Foo": "<=1.0.0", "Bar": ">0.0.0"} }]
|
||||
}]
|
||||
`
|
||||
var all PluginPackages
|
||||
err := json5.Unmarshal([]byte(js), &all)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
selected, err := all.Resolve(PluginVersions{}, PluginDependencies{
|
||||
&PluginDependency{"Bar", semver.MustParseRange(">=1.0.0")},
|
||||
})
|
||||
|
||||
check := func(name, version string) {
|
||||
v := selected.find(name)
|
||||
expected := semver.MustParse(version)
|
||||
if v == nil {
|
||||
t.Errorf("Failed to resolve %s", name)
|
||||
} else if expected.NE(v.Version) {
|
||||
t.Errorf("%s resolved in wrong version %v", name, v)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
check("Foo", "1.5.0")
|
||||
check("Bar", "1.0.0")
|
||||
}
|
||||
|
||||
selected, err = all.Resolve(PluginVersions{}, PluginDependencies{
|
||||
&PluginDependency{"Unresolvable", semver.MustParseRange(">0.0.0")},
|
||||
})
|
||||
if err == nil {
|
||||
t.Error("Unresolvable package resolved:", selected)
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMissingName = errors.New("Missing or empty name field")
|
||||
ErrMissingDesc = errors.New("Missing or empty description field")
|
||||
ErrMissingSite = errors.New("Missing or empty website field")
|
||||
ErrMissingInstall = errors.New("Missing or empty install field")
|
||||
ErrMissingVstr = errors.New("Missing or empty versions field")
|
||||
ErrMissingRequire = errors.New("Missing or empty require field")
|
||||
ErrMissingName = errors.New("Missing or empty name field")
|
||||
ErrMissingDesc = errors.New("Missing or empty description field")
|
||||
ErrMissingSite = errors.New("Missing or empty website field")
|
||||
)
|
||||
|
||||
// PluginInfo contains all the needed info about a plugin
|
||||
@@ -27,19 +24,16 @@ var (
|
||||
// Vstr: version
|
||||
// Require: list of dependencies and requirements
|
||||
type PluginInfo struct {
|
||||
Name string `json:"name"`
|
||||
Desc string `json:"description"`
|
||||
Site string `json:"website"`
|
||||
Install string `json:"install"`
|
||||
Vstr string `json:"version"`
|
||||
Require []string `json:"require"`
|
||||
Name string `json:"Name"`
|
||||
Desc string `json:"Description"`
|
||||
Site string `json:"Website"`
|
||||
}
|
||||
|
||||
// NewPluginInfo parses a JSON input into a valid PluginInfo struct
|
||||
// Returns an error if there are any missing fields or any invalid fields
|
||||
// There are no optional fields in a plugin info json file
|
||||
func NewPluginInfo(data []byte) (*PluginInfo, error) {
|
||||
var info PluginInfo
|
||||
var info []PluginInfo
|
||||
|
||||
dec := json.NewDecoder(bytes.NewReader(data))
|
||||
// dec.DisallowUnknownFields() // Force errors
|
||||
@@ -48,19 +42,5 @@ func NewPluginInfo(data []byte) (*PluginInfo, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if len(info.Name) == 0 {
|
||||
// return nil, ErrMissingName
|
||||
// } else if len(info.Desc) == 0 {
|
||||
// return nil, ErrMissingDesc
|
||||
// } else if len(info.Site) == 0 {
|
||||
// return nil, ErrMissingSite
|
||||
// } else if len(info.Install) == 0 {
|
||||
// return nil, ErrMissingInstall
|
||||
// } else if len(info.Vstr) == 0 {
|
||||
// return nil, ErrMissingVstr
|
||||
// } else if len(info.Require) == 0 {
|
||||
// return nil, ErrMissingRequire
|
||||
// }
|
||||
|
||||
return &info, nil
|
||||
return &info[0], nil
|
||||
}
|
||||
|
||||
@@ -17,10 +17,13 @@ const (
|
||||
RTHelp = 2
|
||||
RTPlugin = 3
|
||||
RTSyntaxHeader = 4
|
||||
NumTypes = 5 // How many filetypes are there
|
||||
)
|
||||
|
||||
type RTFiletype byte
|
||||
var (
|
||||
NumTypes = 5 // How many filetypes are there
|
||||
)
|
||||
|
||||
type RTFiletype int
|
||||
|
||||
// RuntimeFile allows the program to read runtime data like colorschemes or syntax files
|
||||
type RuntimeFile interface {
|
||||
@@ -31,8 +34,21 @@ type RuntimeFile interface {
|
||||
}
|
||||
|
||||
// allFiles contains all available files, mapped by filetype
|
||||
var allFiles [NumTypes][]RuntimeFile
|
||||
var realFiles [NumTypes][]RuntimeFile
|
||||
var allFiles [][]RuntimeFile
|
||||
var realFiles [][]RuntimeFile
|
||||
|
||||
func init() {
|
||||
allFiles = make([][]RuntimeFile, NumTypes)
|
||||
realFiles = make([][]RuntimeFile, NumTypes)
|
||||
}
|
||||
|
||||
// NewRTFiletype creates a new RTFiletype
|
||||
func NewRTFiletype() int {
|
||||
NumTypes++
|
||||
allFiles = append(allFiles, []RuntimeFile{})
|
||||
realFiles = append(realFiles, []RuntimeFile{})
|
||||
return NumTypes - 1
|
||||
}
|
||||
|
||||
// some file on filesystem
|
||||
type realFile string
|
||||
@@ -176,18 +192,22 @@ func InitRuntimeFiles() {
|
||||
for _, f := range srcs {
|
||||
if strings.HasSuffix(f.Name(), ".lua") {
|
||||
p.Srcs = append(p.Srcs, realFile(filepath.Join(plugdir, d.Name(), f.Name())))
|
||||
} else if f.Name() == "info.json" {
|
||||
data, err := ioutil.ReadFile(filepath.Join(plugdir, d.Name(), "info.json"))
|
||||
} else if strings.HasSuffix(f.Name(), ".json") {
|
||||
data, err := ioutil.ReadFile(filepath.Join(plugdir, d.Name(), f.Name()))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
p.Info, _ = NewPluginInfo(data)
|
||||
p.Info, err = NewPluginInfo(data)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
p.Name = p.Info.Name
|
||||
}
|
||||
}
|
||||
|
||||
if !isID(p.Name) {
|
||||
log.Println("Invalid plugin name", p.Name)
|
||||
if !isID(p.Name) || len(p.Srcs) <= 0 {
|
||||
log.Println(p.Name, "is not a plugin")
|
||||
continue
|
||||
}
|
||||
Plugins = append(Plugins, p)
|
||||
@@ -205,17 +225,21 @@ func InitRuntimeFiles() {
|
||||
for _, f := range srcs {
|
||||
if strings.HasSuffix(f, ".lua") {
|
||||
p.Srcs = append(p.Srcs, assetFile(filepath.Join(plugdir, d, f)))
|
||||
} else if f == "info.json" {
|
||||
data, err := Asset(filepath.Join(plugdir, d, "info.json"))
|
||||
} else if strings.HasSuffix(f, ".json") {
|
||||
data, err := Asset(filepath.Join(plugdir, d, f))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
p.Info, _ = NewPluginInfo(data)
|
||||
p.Info, err = NewPluginInfo(data)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
p.Name = p.Info.Name
|
||||
}
|
||||
}
|
||||
if !isID(p.Name) {
|
||||
log.Println("Invalid plugin name", p.Name)
|
||||
if !isID(p.Name) || len(p.Srcs) <= 0 {
|
||||
log.Println(p.Name, "is not a plugin")
|
||||
continue
|
||||
}
|
||||
Plugins = append(Plugins, p)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -34,7 +35,7 @@ func init() {
|
||||
|
||||
// Options with validators
|
||||
var optionValidators = map[string]optionValidator{
|
||||
// "autosave": validateNonNegativeValue,
|
||||
"autosave": validateNonNegativeValue,
|
||||
"tabsize": validatePositiveValue,
|
||||
"scrollmargin": validateNonNegativeValue,
|
||||
"scrollspeed": validateNonNegativeValue,
|
||||
@@ -45,7 +46,7 @@ var optionValidators = map[string]optionValidator{
|
||||
}
|
||||
|
||||
func ReadSettings() error {
|
||||
filename := ConfigDir + "/settings.json"
|
||||
filename := filepath.Join(ConfigDir, "settings.json")
|
||||
if _, e := os.Stat(filename); e == nil {
|
||||
input, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
@@ -57,6 +58,18 @@ func ReadSettings() error {
|
||||
if err != nil {
|
||||
return errors.New("Error reading settings.json: " + err.Error())
|
||||
}
|
||||
|
||||
// check if autosave is a boolean and convert it to float if so
|
||||
if v, ok := parsedSettings["autosave"]; ok {
|
||||
s, ok := v.(bool)
|
||||
if ok {
|
||||
if s {
|
||||
parsedSettings["autosave"] = 8.0
|
||||
} else {
|
||||
parsedSettings["autosave"] = 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -119,12 +132,22 @@ func WriteSettings(filename string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// RegisterCommonOption creates a new option. This is meant to be called by plugins to add options.
|
||||
func RegisterCommonOption(name string, defaultvalue interface{}) error {
|
||||
func OverwriteSettings(filename string) error {
|
||||
var err error
|
||||
if _, e := os.Stat(ConfigDir); e == nil {
|
||||
txt, _ := json.MarshalIndent(GlobalSettings, "", " ")
|
||||
err = ioutil.WriteFile(filename, append(txt, '\n'), 0644)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// RegisterCommonOptionPlug creates a new option (called pl.name). This is meant to be called by plugins to add options.
|
||||
func RegisterCommonOptionPlug(pl string, name string, defaultvalue interface{}) error {
|
||||
name = pl + "." + name
|
||||
if v, ok := GlobalSettings[name]; !ok {
|
||||
defaultCommonSettings[name] = defaultvalue
|
||||
GlobalSettings[name] = defaultvalue
|
||||
err := WriteSettings(ConfigDir + "/settings.json")
|
||||
err := WriteSettings(filepath.Join(ConfigDir, "/settings.json"))
|
||||
if err != nil {
|
||||
return errors.New("Error writing settings.json file: " + err.Error())
|
||||
}
|
||||
@@ -134,11 +157,17 @@ func RegisterCommonOption(name string, defaultvalue interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterGlobalOptionPlug creates a new global-only option (named pl.name)
|
||||
func RegisterGlobalOptionPlug(pl string, name string, defaultvalue interface{}) error {
|
||||
return RegisterGlobalOption(pl+"."+name, defaultvalue)
|
||||
}
|
||||
|
||||
// RegisterGlobalOption creates a new global-only option
|
||||
func RegisterGlobalOption(name string, defaultvalue interface{}) error {
|
||||
if v, ok := GlobalSettings[name]; !ok {
|
||||
defaultGlobalSettings[name] = defaultvalue
|
||||
GlobalSettings[name] = defaultvalue
|
||||
err := WriteSettings(ConfigDir + "/settings.json")
|
||||
err := WriteSettings(filepath.Join(ConfigDir, "settings.json"))
|
||||
if err != nil {
|
||||
return errors.New("Error writing settings.json file: " + err.Error())
|
||||
}
|
||||
@@ -181,7 +210,7 @@ var defaultCommonSettings = map[string]interface{}{
|
||||
"softwrap": false,
|
||||
"splitbottom": true,
|
||||
"splitright": true,
|
||||
"statusformatl": "$(filename) $(modified)($(line),$(col)) | ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding)",
|
||||
"statusformatl": "$(filename) $(modified)($(line),$(col)) $(status.paste)| ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding)",
|
||||
"statusformatr": "$(bind:ToggleKeyMenu): bindings, $(bind:ToggleHelp): help",
|
||||
"statusline": true,
|
||||
"syntax": true,
|
||||
@@ -215,14 +244,16 @@ func DefaultCommonSettings() map[string]interface{} {
|
||||
// a list of settings that should only be globally modified and their
|
||||
// default values
|
||||
var defaultGlobalSettings = map[string]interface{}{
|
||||
// "autosave": float64(0),
|
||||
"colorscheme": "default",
|
||||
"infobar": true,
|
||||
"keymenu": false,
|
||||
"mouse": true,
|
||||
"paste": false,
|
||||
"savehistory": true,
|
||||
"sucmd": "sudo",
|
||||
"autosave": float64(0),
|
||||
"colorscheme": "default",
|
||||
"infobar": true,
|
||||
"keymenu": false,
|
||||
"mouse": true,
|
||||
"paste": false,
|
||||
"savehistory": true,
|
||||
"sucmd": "sudo",
|
||||
"pluginchannels": []string{"https://raw.githubusercontent.com/micro-editor/plugin-channel/master/channel.json"},
|
||||
"pluginrepos": []string{},
|
||||
}
|
||||
|
||||
// a list of settings that should never be globally modified
|
||||
|
||||
@@ -416,7 +416,11 @@ func (w *BufWindow) displayBuffer() {
|
||||
}
|
||||
curNumStyle := config.DefStyle
|
||||
if style, ok := config.Colorscheme["current-line-number"]; ok {
|
||||
curNumStyle = style
|
||||
if !b.Settings["cursorline"].(bool) {
|
||||
curNumStyle = lineNumStyle
|
||||
} else {
|
||||
curNumStyle = style
|
||||
}
|
||||
}
|
||||
|
||||
// We need to know the string length of the largest line number
|
||||
|
||||
@@ -15,6 +15,8 @@ import (
|
||||
type InfoWindow struct {
|
||||
*info.InfoBuf
|
||||
*View
|
||||
|
||||
hscroll int
|
||||
}
|
||||
|
||||
func (i *InfoWindow) errStyle() tcell.Style {
|
||||
@@ -175,6 +177,36 @@ func (i *InfoWindow) displayKeyMenu() {
|
||||
}
|
||||
}
|
||||
|
||||
func (i *InfoWindow) totalSize() int {
|
||||
sum := 0
|
||||
for _, n := range i.Suggestions {
|
||||
sum += runewidth.StringWidth(n) + 1
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
func (i *InfoWindow) scrollToSuggestion() {
|
||||
x := 0
|
||||
s := i.totalSize()
|
||||
|
||||
for j, n := range i.Suggestions {
|
||||
c := utf8.RuneCountInString(n)
|
||||
if j == i.CurSuggestion {
|
||||
if x+c >= i.hscroll+i.Width {
|
||||
i.hscroll = util.Clamp(x+c+1-i.Width, 0, s-i.Width)
|
||||
} else if x < i.hscroll {
|
||||
i.hscroll = util.Clamp(x-1, 0, s-i.Width)
|
||||
}
|
||||
break
|
||||
}
|
||||
x += c + 1
|
||||
}
|
||||
|
||||
if s-i.Width <= 0 {
|
||||
i.hscroll = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (i *InfoWindow) Display() {
|
||||
x := 0
|
||||
if config.GetGlobalOption("keymenu").(bool) {
|
||||
@@ -204,6 +236,11 @@ func (i *InfoWindow) Display() {
|
||||
}
|
||||
|
||||
if i.HasSuggestions && len(i.Suggestions) > 1 {
|
||||
i.scrollToSuggestion()
|
||||
|
||||
x := -i.hscroll
|
||||
done := false
|
||||
|
||||
statusLineStyle := config.DefStyle.Reverse(true)
|
||||
if style, ok := config.Colorscheme["statusline"]; ok {
|
||||
statusLineStyle = style
|
||||
@@ -212,29 +249,43 @@ func (i *InfoWindow) Display() {
|
||||
if config.GetGlobalOption("keymenu").(bool) {
|
||||
keymenuOffset = len(keydisplay)
|
||||
}
|
||||
x := 0
|
||||
|
||||
draw := func(r rune, s tcell.Style) {
|
||||
y := i.Y - keymenuOffset - 1
|
||||
rw := runewidth.RuneWidth(r)
|
||||
for j := 0; j < rw; j++ {
|
||||
c := r
|
||||
if j > 0 {
|
||||
c = ' '
|
||||
}
|
||||
|
||||
if x == i.Width-1 && !done {
|
||||
screen.SetContent(i.Width-1, y, '>', nil, s)
|
||||
x++
|
||||
break
|
||||
} else if x == 0 && i.hscroll > 0 {
|
||||
screen.SetContent(0, y, '<', nil, s)
|
||||
} else if x >= 0 && x < i.Width {
|
||||
screen.SetContent(x, y, c, nil, s)
|
||||
}
|
||||
x++
|
||||
}
|
||||
}
|
||||
|
||||
for j, s := range i.Suggestions {
|
||||
style := statusLineStyle
|
||||
if i.CurSuggestion == j {
|
||||
style = style.Reverse(true)
|
||||
}
|
||||
for _, r := range s {
|
||||
screen.SetContent(x, i.Y-keymenuOffset-1, r, nil, style)
|
||||
x++
|
||||
if x >= i.Width {
|
||||
return
|
||||
}
|
||||
}
|
||||
screen.SetContent(x, i.Y-keymenuOffset-1, ' ', nil, statusLineStyle)
|
||||
x++
|
||||
if x >= i.Width {
|
||||
return
|
||||
draw(r, style)
|
||||
// screen.SetContent(x, i.Y-keymenuOffset-1, r, nil, style)
|
||||
}
|
||||
draw(' ', statusLineStyle)
|
||||
}
|
||||
|
||||
for x < i.Width {
|
||||
screen.SetContent(x, i.Y-keymenuOffset-1, ' ', nil, statusLineStyle)
|
||||
x++
|
||||
draw(' ', statusLineStyle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,9 @@ var statusInfo = map[string]func(*buffer.Buffer) string{
|
||||
if b.Modified() {
|
||||
return "+ "
|
||||
}
|
||||
if b.Type.Readonly {
|
||||
return "[ro] "
|
||||
}
|
||||
return ""
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2,14 +2,8 @@ package info
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/zyedidia/micro/internal/buffer"
|
||||
luar "layeh.com/gopher-luar"
|
||||
|
||||
"github.com/zyedidia/micro/internal/config"
|
||||
ulua "github.com/zyedidia/micro/internal/lua"
|
||||
"github.com/zyedidia/micro/internal/screen"
|
||||
)
|
||||
|
||||
// The InfoBuf displays messages and other info at the bottom of the screen.
|
||||
@@ -134,63 +128,6 @@ func (i *InfoBuf) YNPrompt(prompt string, donecb func(bool, bool)) {
|
||||
i.YNCallback = donecb
|
||||
}
|
||||
|
||||
// PlugPrompt provides a plugin interface for calling "Prompt" with the appropriate Lua callbacks
|
||||
func (i *InfoBuf) PlugPrompt(prompt string, msg string, ptype string, eventcb string, donecb string) {
|
||||
eventLuaFn := strings.Split(eventcb, ".")
|
||||
doneLuaFn := strings.Split(donecb, ".")
|
||||
var luaEventcb func(string)
|
||||
var luaDonecb func(string, bool)
|
||||
|
||||
if len(eventLuaFn) == 2 {
|
||||
plName, plFn := doneLuaFn[0], doneLuaFn[1]
|
||||
pl := config.FindPlugin(plName)
|
||||
if pl != nil {
|
||||
luaEventcb = func(resp string) {
|
||||
_, err := pl.Call(plFn, luar.New(ulua.L, resp))
|
||||
if err != nil && err != config.ErrNoSuchFunction {
|
||||
screen.TermMessage(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(doneLuaFn) == 2 {
|
||||
plName, plFn := doneLuaFn[0], doneLuaFn[1]
|
||||
pl := config.FindPlugin(plName)
|
||||
if pl != nil {
|
||||
luaDonecb = func(resp string, canceled bool) {
|
||||
_, err := pl.Call(plFn, luar.New(ulua.L, resp), luar.New(ulua.L, canceled))
|
||||
if err != nil && err != config.ErrNoSuchFunction {
|
||||
screen.TermMessage(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i.Prompt(prompt, msg, ptype, luaEventcb, luaDonecb)
|
||||
}
|
||||
|
||||
// PlugYNPrompt provides a plugin interface for calling "YNPrompt" with the appropriate Lua callbacks
|
||||
func (i *InfoBuf) PlugYNPrompt(prompt string, donecb string) {
|
||||
doneLuaFn := strings.Split(donecb, ".")
|
||||
var luaDonecb func(bool, bool)
|
||||
|
||||
if len(doneLuaFn) == 2 {
|
||||
plName, plFn := doneLuaFn[0], doneLuaFn[1]
|
||||
pl := config.FindPlugin(plName)
|
||||
if pl != nil {
|
||||
luaDonecb = func(resp bool, canceled bool) {
|
||||
_, err := pl.Call(plFn, luar.New(ulua.L, resp), luar.New(ulua.L, canceled))
|
||||
if err != nil && err != config.ErrNoSuchFunction {
|
||||
screen.TermMessage(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i.YNPrompt(prompt, luaDonecb)
|
||||
}
|
||||
|
||||
// DonePrompt finishes the current prompt and indicates whether or not it was canceled
|
||||
func (i *InfoBuf) DonePrompt(canceled bool) {
|
||||
hadYN := i.HasYN
|
||||
|
||||
@@ -4,14 +4,6 @@ import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
luar "layeh.com/gopher-luar"
|
||||
|
||||
lua "github.com/yuin/gopher-lua"
|
||||
"github.com/zyedidia/micro/internal/config"
|
||||
ulua "github.com/zyedidia/micro/internal/lua"
|
||||
"github.com/zyedidia/micro/internal/screen"
|
||||
)
|
||||
|
||||
var Jobs chan JobFunction
|
||||
@@ -32,7 +24,7 @@ func init() {
|
||||
// JobFunction is a representation of a job (this data structure is what is loaded
|
||||
// into the jobs channel)
|
||||
type JobFunction struct {
|
||||
Function func(string, ...interface{})
|
||||
Function func(string, []interface{})
|
||||
Output string
|
||||
Args []interface{}
|
||||
}
|
||||
@@ -41,7 +33,7 @@ type JobFunction struct {
|
||||
type CallbackFile struct {
|
||||
io.Writer
|
||||
|
||||
callback func(string, ...interface{})
|
||||
callback func(string, []interface{})
|
||||
args []interface{}
|
||||
}
|
||||
|
||||
@@ -55,23 +47,23 @@ func (f *CallbackFile) Write(data []byte) (int, error) {
|
||||
|
||||
// JobStart starts a shell command in the background with the given callbacks
|
||||
// It returns an *exec.Cmd as the job id
|
||||
func JobStart(cmd string, onStdout, onStderr, onExit string, userargs ...interface{}) *exec.Cmd {
|
||||
func JobStart(cmd string, onStdout, onStderr, onExit func(string, []interface{}), userargs ...interface{}) *exec.Cmd {
|
||||
return JobSpawn("sh", []string{"-c", cmd}, onStdout, onStderr, onExit, userargs...)
|
||||
}
|
||||
|
||||
// JobSpawn starts a process with args in the background with the given callbacks
|
||||
// It returns an *exec.Cmd as the job id
|
||||
func JobSpawn(cmdName string, cmdArgs []string, onStdout, onStderr, onExit string, userargs ...interface{}) *exec.Cmd {
|
||||
func JobSpawn(cmdName string, cmdArgs []string, onStdout, onStderr, onExit func(string, []interface{}), userargs ...interface{}) *exec.Cmd {
|
||||
// Set up everything correctly if the functions have been provided
|
||||
proc := exec.Command(cmdName, cmdArgs...)
|
||||
var outbuf bytes.Buffer
|
||||
if onStdout != "" {
|
||||
proc.Stdout = &CallbackFile{&outbuf, luaFunctionJob(onStdout), userargs}
|
||||
if onStdout != nil {
|
||||
proc.Stdout = &CallbackFile{&outbuf, onStdout, userargs}
|
||||
} else {
|
||||
proc.Stdout = &outbuf
|
||||
}
|
||||
if onStderr != "" {
|
||||
proc.Stderr = &CallbackFile{&outbuf, luaFunctionJob(onStderr), userargs}
|
||||
if onStderr != nil {
|
||||
proc.Stderr = &CallbackFile{&outbuf, onStderr, userargs}
|
||||
} else {
|
||||
proc.Stderr = &outbuf
|
||||
}
|
||||
@@ -79,7 +71,7 @@ func JobSpawn(cmdName string, cmdArgs []string, onStdout, onStderr, onExit strin
|
||||
go func() {
|
||||
// Run the process in the background and create the onExit callback
|
||||
proc.Run()
|
||||
jobFunc := JobFunction{luaFunctionJob(onExit), string(outbuf.Bytes()), userargs}
|
||||
jobFunc := JobFunction{onExit, string(outbuf.Bytes()), userargs}
|
||||
Jobs <- jobFunc
|
||||
}()
|
||||
|
||||
@@ -100,25 +92,3 @@ func JobSend(cmd *exec.Cmd, data string) {
|
||||
|
||||
stdin.Write([]byte(data))
|
||||
}
|
||||
|
||||
// luaFunctionJob returns a function that will call the given lua function
|
||||
// structured as a job call i.e. the job output and arguments are provided
|
||||
// to the lua function
|
||||
func luaFunctionJob(fn string) func(string, ...interface{}) {
|
||||
luaFn := strings.Split(fn, ".")
|
||||
if len(luaFn) <= 1 {
|
||||
return nil
|
||||
}
|
||||
plName, plFn := luaFn[0], luaFn[1]
|
||||
pl := config.FindPlugin(plName)
|
||||
if pl == nil {
|
||||
return nil
|
||||
}
|
||||
return func(output string, args ...interface{}) {
|
||||
luaArgs := []lua.LValue{luar.New(ulua.L, output), luar.New(ulua.L, args)}
|
||||
_, err := pl.Call(plFn, luaArgs...)
|
||||
if err != nil && err != config.ErrNoSuchFunction {
|
||||
screen.TermMessage(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,19 +2,12 @@ package shell
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
lua "github.com/yuin/gopher-lua"
|
||||
"github.com/zyedidia/micro/internal/buffer"
|
||||
"github.com/zyedidia/micro/internal/config"
|
||||
ulua "github.com/zyedidia/micro/internal/lua"
|
||||
"github.com/zyedidia/micro/internal/screen"
|
||||
"github.com/zyedidia/terminal"
|
||||
luar "layeh.com/gopher-luar"
|
||||
)
|
||||
|
||||
type TermType int
|
||||
@@ -76,7 +69,7 @@ func (t *Terminal) GetSelection(width int) string {
|
||||
}
|
||||
|
||||
// Start begins a new command in this terminal with a given view
|
||||
func (t *Terminal) Start(execCmd []string, getOutput bool, wait bool, callback string, userargs []interface{}) error {
|
||||
func (t *Terminal) Start(execCmd []string, getOutput bool, wait bool, callback func(out string, userargs []interface{}), userargs []interface{}) error {
|
||||
if len(execCmd) <= 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -95,27 +88,16 @@ func (t *Terminal) Start(execCmd []string, getOutput bool, wait bool, callback s
|
||||
t.Status = TTRunning
|
||||
t.title = execCmd[0] + ":" + strconv.Itoa(cmd.Process.Pid)
|
||||
t.wait = wait
|
||||
|
||||
luaFn := strings.Split(callback, ".")
|
||||
if len(luaFn) >= 2 {
|
||||
plName, plFn := luaFn[0], luaFn[1]
|
||||
pl := config.FindPlugin(plName)
|
||||
if pl != nil {
|
||||
t.callback = func(out string) {
|
||||
luaArgs := []lua.LValue{luar.New(ulua.L, out), luar.New(ulua.L, userargs)}
|
||||
_, err := pl.Call(plFn, luaArgs...)
|
||||
if err != nil {
|
||||
screen.TermMessage(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
t.callback = func(out string) {
|
||||
callback(out, userargs)
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
err := Term.Parse()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "[Press enter to close]")
|
||||
Term.Write([]byte("Press enter to close"))
|
||||
screen.Redraw()
|
||||
break
|
||||
}
|
||||
screen.Redraw()
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// LuaRuneAt is a helper function for lua plugins to return the rune
|
||||
// at an index within a string
|
||||
func LuaRuneAt(str string, runeidx int) string {
|
||||
i := 0
|
||||
for len(str) > 0 {
|
||||
@@ -20,6 +22,7 @@ func LuaRuneAt(str string, runeidx int) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// LuaGetLeadingWhitespace returns the leading whitespace of a string (used by lua plugins)
|
||||
func LuaGetLeadingWhitespace(s string) string {
|
||||
ws := []byte{}
|
||||
for len(s) > 0 {
|
||||
@@ -35,6 +38,7 @@ func LuaGetLeadingWhitespace(s string) string {
|
||||
return string(ws)
|
||||
}
|
||||
|
||||
// LuaIsWordChar returns true if the first rune in a string is a word character
|
||||
func LuaIsWordChar(s string) bool {
|
||||
r, _ := utf8.DecodeRuneInString(s)
|
||||
return IsWordChar(r)
|
||||
|
||||
@@ -417,3 +417,8 @@ func IsNonAlphaNumeric(c rune) bool {
|
||||
func ParseSpecial(s string) string {
|
||||
return strings.Replace(s, "\\t", "\t", -1)
|
||||
}
|
||||
|
||||
// String converts a byte array to a string (for lua plugins)
|
||||
func String(s []byte) string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
@@ -456,9 +456,9 @@ func (n *Node) unsplit(i int, h bool) {
|
||||
|
||||
// Unsplit deletes this split and resizes everything
|
||||
// else accordingly
|
||||
func (n *Node) Unsplit() {
|
||||
if !n.IsLeaf() {
|
||||
return
|
||||
func (n *Node) Unsplit() bool {
|
||||
if !n.IsLeaf() || n.parent == nil {
|
||||
return false
|
||||
}
|
||||
ind := 0
|
||||
for i, c := range n.parent.children {
|
||||
@@ -473,8 +473,9 @@ func (n *Node) Unsplit() {
|
||||
}
|
||||
|
||||
if n.parent.IsLeaf() {
|
||||
n.parent.Unsplit()
|
||||
return n.parent.Unsplit()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// String returns the string form of the node and all children (used for debugging)
|
||||
|
||||
@@ -20,5 +20,5 @@ color-link line-number "246,254"
|
||||
color-link cursor-line "254"
|
||||
color-link color-column "254"
|
||||
#No extended types (bool in C, &c.) and plain brackets
|
||||
color-link type.extended "default"
|
||||
color-link symbol.brackets "default"
|
||||
color-link type.extended "241,231"
|
||||
color-link symbol.brackets "241,231"
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
#CaptainMcClellan's personal color scheme.
|
||||
#Paper version
|
||||
color-link default "black,white"
|
||||
color-link comment "bold black"
|
||||
color-link constant "cyan"
|
||||
color-link constant.bool "bold cyan"
|
||||
color-link constant.bool.true "bold green"
|
||||
color-link constant.bool.false "bold red"
|
||||
color-link constant.string "bold yellow"
|
||||
color-link constant.string.url "underline blue, white"
|
||||
color-link constant.number "constant"
|
||||
color-link constant.specialChar "bold magenta"
|
||||
color-link identifier "bold red"
|
||||
color-link identifier.macro "bold red"
|
||||
color-link identifier.var "bold blue"
|
||||
color-link identifier.class "bold green"
|
||||
color-link preproc "bold cyan"
|
||||
color-link statement "bold yellow"
|
||||
color-link symbol "red"
|
||||
color-link symbol.brackets "blue"
|
||||
color-link type "green"
|
||||
color-link type.keyword "bold green"
|
||||
color-link special "magenta"
|
||||
color-link ignore "default"
|
||||
color-link error ",brightred"
|
||||
color-link todo "black,brightyellow"
|
||||
color-link indent-char ",brightgreen"
|
||||
color-link line-number "green"
|
||||
color-link line-number.scrollbar "green"
|
||||
color-link statusline "white,blue"
|
||||
color-link tabbar "white,blue"
|
||||
color-link current-line-number "red"
|
||||
color-link current-line-number.scroller "red"
|
||||
color-link gutter-error ",red"
|
||||
color-link gutter-warning "red"
|
||||
color-link color-column "cyan"
|
||||
color-link underlined.url "underline blue, white"
|
||||
@@ -15,12 +15,12 @@ color-link todo "bold #D33682,#242424"
|
||||
color-link statusline "#242424,#CCCCCC"
|
||||
color-link tabbar "#242424,#CCCCCC"
|
||||
color-link indent-char "#4F4F4F,#242424"
|
||||
color-link line-number "#666666,#242424"
|
||||
color-link line-number "#666666,#2C2C2C"
|
||||
color-link current-line-number "#666666,#242424"
|
||||
color-link gutter-error "#CB4B16,#242424"
|
||||
color-link gutter-warning "#E6DB74,#242424"
|
||||
color-link cursor-line "default,#2C2C2C"
|
||||
color-link color-column "default,#2C2C2C"
|
||||
color-link cursor-line "#2C2C2C"
|
||||
color-link color-column "#2C2C2C"
|
||||
#No extended types; Plain brackets.
|
||||
color-link type.extended "default"
|
||||
#color-link symbol.brackets "default"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#Geany
|
||||
color-link comment "red"
|
||||
color-link constant "default"
|
||||
color-link constant.number
|
||||
color-link constant.string "bold yellow"
|
||||
color-link identifier "default"
|
||||
color-link preproc "cyan"
|
||||
@@ -20,4 +19,4 @@ color-link statusline "black,white"
|
||||
color-link tabbar "black,white"
|
||||
color-link color-column "bold geren"
|
||||
color-link gutter-error ",red"
|
||||
color-link gutter-warning "red"
|
||||
color-link gutter-warning "red"
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
#True color theme based on Github's syntax highlighting.
|
||||
#Warning, this is based on how it rendered in my Firefox!
|
||||
#Yours may look different.
|
||||
color-link comment "bold #969896"
|
||||
color-link constant "#0086B9"
|
||||
color-link constant.number "#0086B9"
|
||||
color-link constant.specialChar "bold #1836BD"
|
||||
color-link constant.string "bold #1836BD"
|
||||
color-link constant.bool "#0086B9"
|
||||
color-link identifier "#A71D5D"
|
||||
color-link preproc "bold #A71D5D"
|
||||
color-link special "#A71D5D"
|
||||
color-link statement "#A71D5D"
|
||||
color-link symbol "default"
|
||||
color-link type "#A71D5D"
|
||||
color-link error "bold ,#E34234"
|
||||
color-link todo "white"
|
||||
color-link indent-char "default"
|
||||
color-link line-number "bold #969896"
|
||||
color-link current-line-number "bold #969896"
|
||||
color-link gutter-error "bold ,#E34234"
|
||||
color-link gutter-warning "bold #f26522"
|
||||
color-link statusline "bold #c8c9cb,#24292e"
|
||||
color-link tabbar "bold #c8c9cb,#24292e"
|
||||
22
runtime/colorschemes/gotham.micro
Normal file
22
runtime/colorschemes/gotham.micro
Normal file
@@ -0,0 +1,22 @@
|
||||
color-link default "#99D1CE,#0C1014"
|
||||
color-link comment "#245361,#0C1014"
|
||||
color-link identifier "#599CAB,#0C1014"
|
||||
color-link constant "#D26937,#0C1014"
|
||||
color-link constant.string "#2AA889,#0C1014"
|
||||
color-link constant.string.char "#D3EBE9,#0C1014"
|
||||
color-link statement "#599CAB,#0C1014"
|
||||
color-link preproc "#C23127,#0C1014"
|
||||
color-link type "#D26937,#0C1014"
|
||||
color-link special "#D26937,#0C1014"
|
||||
color-link underlined "#EDB443,#0C1014"
|
||||
color-link error "bold #C23127,#0C1014"
|
||||
color-link todo "bold #888CA6,#0C1014"
|
||||
color-link statusline "#091F2E,#599CAB"
|
||||
color-link indent-char "#505050,#0C1014"
|
||||
color-link line-number "#245361,#11151C"
|
||||
color-link current-line-number "#599CAB,#11151C"
|
||||
color-link gutter-error "#C23127,#11151C"
|
||||
color-link gutter-warning "#EDB443,#11151C"
|
||||
color-link cursor-line "#091F2E"
|
||||
color-link color-column "#11151C"
|
||||
color-link symbol "#99D1CE,#0C1014"
|
||||
@@ -13,8 +13,8 @@ color-link underlined "underline #282828"
|
||||
color-link error "#9d0006,#282828"
|
||||
color-link gutter-error "#fb4934,#282828"
|
||||
color-link gutter-warning "#d79921,#282828"
|
||||
color-link line-number "#665c54,#282828"
|
||||
color-link current-line-number "#665c54,#3c3836"
|
||||
color-link line-number "#665c54,#3c3836"
|
||||
color-link current-line-number "#d79921,#282828"
|
||||
color-link cursor-line "#3c3836"
|
||||
color-link color-column "#79740e"
|
||||
color-link statusline "#ebdbb2,#665c54"
|
||||
|
||||
@@ -12,8 +12,8 @@ color-link underlined "underline 109,235"
|
||||
color-link error "235,124"
|
||||
color-link todo "bold 223,235"
|
||||
color-link line-number "243,237"
|
||||
color-link current-line-number "172,237"
|
||||
color-link current-line-number "172,235"
|
||||
color-link cursor-line "237"
|
||||
color-link color-column "237"
|
||||
color-link statusline "223,237"
|
||||
color-link tabbar "223,237"
|
||||
color-link tabbar "223,237"
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
#A colorscheme based on Code::Blocks IDE
|
||||
#but with a white background.
|
||||
color-link default "black,white"
|
||||
color-link comment "bold black"
|
||||
color-link constant "blue"
|
||||
color-link constant.number "bold magenta"
|
||||
color-link constant.string "bold blue"
|
||||
color-link identifier "black"
|
||||
color-link preproc "green"
|
||||
color-link statement "blue"
|
||||
color-link symbol "red"
|
||||
color-link symbol.brackets "blue"
|
||||
color-link type "blue"
|
||||
color-link special "magenta"
|
||||
color-link ignore "default"
|
||||
color-link error "bold white,brightred"
|
||||
color-link todo "bold black,brightyellow"
|
||||
color-link indent-char "bold black"
|
||||
color-link line-number "black,white"
|
||||
color-link statusline "white,red"
|
||||
color-link tabbar "white,red"
|
||||
color-link current-line-number "red,black"
|
||||
color-link gutter-error ",red"
|
||||
color-link gutter-warning "red"
|
||||
color-link color-column "black"
|
||||
@@ -1,23 +0,0 @@
|
||||
#Theme based on Code::Blocks IDE's default syntax highlighting.
|
||||
color-link comment "bold black"
|
||||
color-link constant "blue"
|
||||
color-link constant.string "bold blue"
|
||||
color-link constant.number "bold magenta"
|
||||
color-link identifier "default"
|
||||
color-link preproc "green"
|
||||
color-link statement "blue"
|
||||
color-link symbol "red"
|
||||
color-link symbol.brackets "blue"
|
||||
color-link type "blue"
|
||||
color-link special "magenta"
|
||||
color-link ignore "default"
|
||||
color-link error ",brightred"
|
||||
color-link todo "bold black,brightyellow"
|
||||
color-link indent-char "bold black"
|
||||
color-link line-number "black,white"
|
||||
color-link statusline "white,red"
|
||||
color-link tabbar "white,red"
|
||||
color-link current-line-number "red"
|
||||
color-link gutter-error ",red"
|
||||
color-link gutter-warning "red"
|
||||
color-link color-column "white"
|
||||
@@ -1,31 +0,0 @@
|
||||
#Funky Cactus theme
|
||||
color-link comment "bold black"
|
||||
color-link constant "cyan"
|
||||
color-link constant.bool "bold cyan"
|
||||
color-link constant.bool.true "bold green"
|
||||
color-link constant.bool.false "bold red"
|
||||
color-link constant.string "yellow"
|
||||
color-link constant.number "constant"
|
||||
color-link constant.specialChar "bold magenta"
|
||||
color-link identifier "bold red"
|
||||
color-link identifier.macro "bold red"
|
||||
color-link identifier.var "bold blue"
|
||||
color-link identifier.class "bold green"
|
||||
color-link preproc "bold cyan"
|
||||
color-link statement "bold yellow"
|
||||
color-link symbol "red"
|
||||
color-link symbol.brackets "blue"
|
||||
color-link type "green"
|
||||
color-link type.keyword "bold green"
|
||||
color-link special "magenta"
|
||||
color-link ignore "default"
|
||||
color-link error "bold ,brightred"
|
||||
color-link todo "underline ,brightyellow"
|
||||
color-link indent-char "bold ,brightgreen"
|
||||
color-link line-number "green"
|
||||
color-link statusline "black,green"
|
||||
color-link tabbar "black,magenta"
|
||||
color-link current-line-number "bold magenta"
|
||||
color-link gutter-error ",red"
|
||||
color-link gutter-warning "red"
|
||||
color-link color-column "bold green"
|
||||
@@ -1,23 +0,0 @@
|
||||
#Gameboy theme
|
||||
color-link default "#3f3f3f,#bfc180"
|
||||
color-link comment "#7d7343"
|
||||
color-link constant "#7d7343"
|
||||
color-link identifier "#ddde7d"
|
||||
color-link preproc "#ddde7d,#7d7343"
|
||||
color-link special "#7d7343"
|
||||
color-link statement "#7d7343"
|
||||
color-link symbol "#7d7343"
|
||||
color-link type "#7d7343"
|
||||
color-link error "#ddde7d,#7d7343"
|
||||
color-link todo "#7d7343,#ddde7d"
|
||||
color-link statusline "#ddde7d,#7d7343"
|
||||
color-link tabbar "#ddde7d,#7d7343"
|
||||
color-link color-column "#7d7343"
|
||||
color-link line-number "#ddde7d,#7d7343"
|
||||
color-link current-line-number "#3f3f3f,#bfc180"
|
||||
color-link gutter-error "#ddde7d,#7d7343"
|
||||
color-link gutter-warning "default"
|
||||
#3f3f3f
|
||||
#7d7343
|
||||
#bfc180
|
||||
#ddde76
|
||||
@@ -1,21 +0,0 @@
|
||||
#Geany Alternate theme
|
||||
color-link default "#000000,#fefefe"
|
||||
color-link comment "#808080"
|
||||
color-link constant "default"
|
||||
color-link constant.bool "#003030"
|
||||
color-link constant.number "#300008"
|
||||
color-link constant.string "#008000"
|
||||
color-link identifier "default"
|
||||
color-link preproc "#bbbb77"
|
||||
color-link special "#003030"
|
||||
color-link statement "#003030"
|
||||
color-link symbol "#300008"
|
||||
color-link symbol.tag "bold #4e9d71"
|
||||
color-link type "#003030"
|
||||
color-link error "#a52a2a"
|
||||
color-link todo "#ffa500"
|
||||
color-link line-number "#000000,#d0d0d0"
|
||||
color-link current-line-number "#000000,#d0d0d0"
|
||||
color-link color-column "#c2ebc2"
|
||||
color-link cursor-line "#f0f0f0"
|
||||
color-link type.extended "default"
|
||||
@@ -1,24 +0,0 @@
|
||||
#Theme based on Github's syntax highlighting.
|
||||
color-link comment "bold black"
|
||||
color-link constant "cyan"
|
||||
color-link constant.number "cyan"
|
||||
color-link constant.specialChar "bold blue"
|
||||
color-link constant.string "bold blue"
|
||||
color-link constant.bool "cyan"
|
||||
color-link identifier "magenta"
|
||||
color-link preproc "bold magenta"
|
||||
color-link special "magenta"
|
||||
color-link statement "magenta"
|
||||
color-link symbol "default"
|
||||
color-link type "magenta"
|
||||
color-link error "bold ,brightred"
|
||||
color-link todo "white"
|
||||
color-link indent-char "default"
|
||||
color-link line-number "bold black"
|
||||
color-link current-line-number "bold black"
|
||||
color-link gutter-error ",red"
|
||||
color-link gutter-warning "bold yellow"
|
||||
color-link statusline "bold white,black"
|
||||
color-link tabbar "bold white,black"
|
||||
#Plain brackets.
|
||||
#color-link symbol.brackets "default"
|
||||
@@ -1,25 +0,0 @@
|
||||
#Midnight Commander inspired theme.
|
||||
color-link default "white,blue"
|
||||
color-link comment "bold black"
|
||||
color-link constant "bold white"
|
||||
color-link constant.string "bold yellow"
|
||||
color-link identifier "bold red"
|
||||
color-link statement "bold cyan"
|
||||
color-link symbol "white"
|
||||
color-link symbol.brackets "white"
|
||||
color-link symbol.tag "bold green"
|
||||
color-link preproc "black,cyan"
|
||||
color-link type "green"
|
||||
color-link special "magenta"
|
||||
color-link ignore "default"
|
||||
color-link error ",brightred"
|
||||
color-link todo ",brightyellow"
|
||||
color-link indent-char ",cyan"
|
||||
color-link line-number "green"
|
||||
color-link statusline "black,cyan"
|
||||
color-link tabbar "black,cyan"
|
||||
color-link current-line-number "black,cyan"
|
||||
color-link cursor-line "black,cyan"
|
||||
color-link gutter-error ",red"
|
||||
color-link gutter-warning "red"
|
||||
color-link color-column "cyan"
|
||||
@@ -1,5 +0,0 @@
|
||||
#Monochrome Paper theme.
|
||||
#Edit your files on a white background without colors.
|
||||
color-link default "black,white"
|
||||
color-link statusline "white,black"
|
||||
color-link tabbar "white,black"
|
||||
@@ -1,3 +0,0 @@
|
||||
#Monochrome
|
||||
#This makes micro use only the terminal's default
|
||||
# foreground and background colours.
|
||||
@@ -1,30 +0,0 @@
|
||||
#Colorscheme styled after default Debian nano.
|
||||
color-link comment "bold blue"
|
||||
color-link comment.bright "cyan"
|
||||
color-link constant "red"
|
||||
color-link constant.bool "yellow"
|
||||
color-link constant.bool.true "bold green"
|
||||
color-link constant.bool.false "bold red"
|
||||
color-link constant.number "default"
|
||||
color-link constant.specialChar "bold magenta"
|
||||
color-link constant.string "bold yellow"
|
||||
color-link identifier "bold blue"
|
||||
color-link identifier.macro "bold red"
|
||||
color-link statement "bold green"
|
||||
color-link symbol "green"
|
||||
#color-link symbol.tag "blue"
|
||||
color-link preproc "brightcyan"
|
||||
color-link type "green"
|
||||
color-link special "magenta"
|
||||
color-link ignore "default"
|
||||
color-link error "white,black"
|
||||
color-link todo "bold cyan"
|
||||
color-link indent-char ",green"
|
||||
color-link line-number "default"
|
||||
color-link current-line-number "default"
|
||||
color-link gutter-error ",white"
|
||||
color-link gutter-warning "white"
|
||||
color-link cursor-line "default"
|
||||
color-link color-column "white"
|
||||
#No extended types ( bool in C ); Plain brackets
|
||||
color-link type.extended "default"
|
||||
@@ -1,22 +0,0 @@
|
||||
#Paper theme, true color edition
|
||||
#Edit on an *actual* white background!
|
||||
color-link default "#000000,#efefef"
|
||||
color-link comment ""
|
||||
color-link constant ""
|
||||
color-link constant.string ""
|
||||
color-link constant.string.url "underline #0000dd"
|
||||
color-link identifier ""
|
||||
color-link identifier.var ""
|
||||
color-link special ""
|
||||
color-link statement ""
|
||||
color-link symbol ""
|
||||
color-link symbol.brackets ""
|
||||
color-link symbol.tag ""
|
||||
color-link type ""
|
||||
color-link statusline ""
|
||||
color-link tabbar ""
|
||||
color-link error ""
|
||||
color-link todo ""
|
||||
color-link color-column ""
|
||||
color-link gutter-error ""
|
||||
color-link gutter-warning ""
|
||||
@@ -1,27 +0,0 @@
|
||||
#Paper theme, Edit on a white background.
|
||||
color-link default "black,white"
|
||||
color-link comment "bold black"
|
||||
color-link constant "cyan"
|
||||
color-link constant.string "bold green"
|
||||
color-link identifier "blue"
|
||||
color-link identifier.macro "bold red"
|
||||
color-link identifier.var "bold blue"
|
||||
color-link identifier.class "bold green"
|
||||
color-link statement "green"
|
||||
color-link symbol "red"
|
||||
color-link symbol.brackets "default"
|
||||
color-link symbol.tag "bold blue"
|
||||
color-link preproc "bold cyan"
|
||||
color-link type "green"
|
||||
color-link special "magenta"
|
||||
color-link ignore "default"
|
||||
color-link error ",brightred"
|
||||
color-link todo ",brightyellow"
|
||||
color-link indent-char ",brightgreen"
|
||||
color-link line-number "black"
|
||||
color-link statusline "white,black"
|
||||
color-link tabbar "white,black"
|
||||
color-link current-line-number "blue"
|
||||
color-link gutter-error ",red"
|
||||
color-link gutter-warning "red"
|
||||
color-link color-column "black"
|
||||
@@ -1,23 +0,0 @@
|
||||
#Symbian
|
||||
color-link default "#000000,#ff8a00"
|
||||
color-link comment "#8c0000"
|
||||
color-link constant "#8c0000"
|
||||
color-link identifier "#ffff8c"
|
||||
color-link preproc "#ffff8c,#8c0000"
|
||||
color-link special "#8c0000"
|
||||
color-link statement "#8c0000"
|
||||
color-link symbol "#8c0000"
|
||||
color-link type "#8c0000"
|
||||
color-link error "#ffff8c,#8c0000"
|
||||
color-link todo "#8c0000,#ffff8c"
|
||||
color-link statusline "#ffff8c,#8c0000"
|
||||
color-link tabbar "#ffff8c,#8c0000"
|
||||
color-link color-column "#8c0000"
|
||||
color-link line-number "#ffff8c,#8c0000"
|
||||
color-link current-line-number "#000000,#ff8a00"
|
||||
color-link gutter-error "#ffff8c,#8c0000"
|
||||
color-link gutter-warning "default"
|
||||
#000000
|
||||
#8c0000
|
||||
#ff8a00
|
||||
#ffff8c
|
||||
@@ -5,7 +5,7 @@ color-link constant.number "#F78C6A,#263238"
|
||||
color-link constant.specialChar "#89DDF3,#263238"
|
||||
color-link constant.string "#C3E88D,#263238"
|
||||
color-link current-line-number "#80DEEA,#263238"
|
||||
color-link cursor-line "#3b4d56"
|
||||
color-link cursor-line "#283942"
|
||||
color-link default "#EEFFFF,#263238"
|
||||
color-link divider "#263238,#80DEEA"
|
||||
color-link error "bold #263238,#F07178"
|
||||
@@ -14,7 +14,7 @@ color-link gutter-warning "#EEFFFF,#FFF176"
|
||||
color-link identifier "#82AAFF,#263238"
|
||||
color-link identifier.macro "#FFCB6B,#263238"
|
||||
color-link indent-char "#505050,#263238"
|
||||
color-link line-number "#656866,#263238"
|
||||
color-link line-number "#656866,#283942"
|
||||
color-link preproc "#C792EA,#263238"
|
||||
color-link special "#C792EA,#263238"
|
||||
color-link statement "#C792EA,#263238"
|
||||
|
||||
21
runtime/colorschemes/monokai-dark.micro
Normal file
21
runtime/colorschemes/monokai-dark.micro
Normal file
@@ -0,0 +1,21 @@
|
||||
color-link default "#D5D8D6,#1D0000"
|
||||
color-link comment "#75715E"
|
||||
color-link identifier "#66D9EF"
|
||||
color-link constant "#AE81FF"
|
||||
color-link constant.string "#E6DB74"
|
||||
color-link constant.string.char "#BDE6AD"
|
||||
color-link statement "#F92672"
|
||||
color-link preproc "#CB4B16"
|
||||
color-link type "#66D9EF"
|
||||
color-link special "#A6E22E"
|
||||
color-link underlined "#D33682"
|
||||
color-link error "bold #CB4B16"
|
||||
color-link todo "bold #D33682"
|
||||
color-link statusline "#282828,#F8F8F2"
|
||||
color-link indent-char "#505050,#282828"
|
||||
color-link line-number "#AAAAAA,#282828"
|
||||
color-link current-line-number "#AAAAAA,#1D0000"
|
||||
color-link gutter-error "#CB4B16"
|
||||
color-link gutter-warning "#E6DB74"
|
||||
color-link cursor-line "#323232"
|
||||
color-link color-column "#323232"
|
||||
32
runtime/colorschemes/one-dark.micro
Normal file
32
runtime/colorschemes/one-dark.micro
Normal file
@@ -0,0 +1,32 @@
|
||||
color-link default "#ABB2BF,#21252C"
|
||||
color-link color-column "#282C34"
|
||||
color-link comment "#5C6370"
|
||||
color-link constant "#C678DD"
|
||||
color-link constant.number "#E5C07B"
|
||||
color-link constant.string "#98C379"
|
||||
color-link constant.string.char "#BDE6AD"
|
||||
color-link constant.specialChar "#DDF2A4"
|
||||
color-link current-line-number "#C6C6C6,#21252C"
|
||||
color-link cursor-line "#282C34"
|
||||
color-link divider "#1E1E1E"
|
||||
color-link error "#D2A8A1"
|
||||
color-link gutter-error "#9B859D"
|
||||
color-link gutter-warning "#9B859D"
|
||||
color-link identifier "#61AFEF"
|
||||
color-link identifier.class "#C678DD"
|
||||
color-link identifier.var "#C678DD"
|
||||
color-link indent-char "#515151"
|
||||
color-link line-number "#636D83,#282C34"
|
||||
color-link preproc "#E0C589"
|
||||
color-link special "#E0C589"
|
||||
color-link statement "#C678DD"
|
||||
color-link statusline "#282828,#ABB2BF"
|
||||
color-link symbol "#AC885B"
|
||||
color-link symbol.brackets "#ABB2BF"
|
||||
color-link symbol.operator "#C678DD"
|
||||
color-link symbol.tag "#AC885B"
|
||||
color-link tabbar "#F2F0EC,#2D2D2D"
|
||||
color-link todo "#8B98AB"
|
||||
color-link type "#66D9EF"
|
||||
color-link type.keyword "#C678DD"
|
||||
color-link underlined "#8996A8"
|
||||
22
runtime/colorschemes/sunny-day.micro
Normal file
22
runtime/colorschemes/sunny-day.micro
Normal file
@@ -0,0 +1,22 @@
|
||||
color-link default "0,230"
|
||||
color-link comment "244"
|
||||
color-link constant.string "17"
|
||||
color-link constant "88"
|
||||
color-link identifier "22"
|
||||
color-link statement "0,230"
|
||||
color-link symbol "89"
|
||||
color-link preproc "22"
|
||||
color-link type "88"
|
||||
color-link special "22"
|
||||
color-link underlined "61,230"
|
||||
color-link error "88"
|
||||
color-link todo "210"
|
||||
color-link statusline "233,229"
|
||||
color-link tabbar "233,229"
|
||||
color-link indent-char "229"
|
||||
color-link line-number "244"
|
||||
color-link gutter-error "88"
|
||||
color-link gutter-warning "88"
|
||||
color-link cursor-line "229"
|
||||
#color-link color-column "196"
|
||||
color-link current-line-number "246"
|
||||
@@ -3,11 +3,12 @@
|
||||
This help page aims to cover two aspects of micro's syntax highlighting engine:
|
||||
|
||||
* How to create colorschemes and use them.
|
||||
* How to create syntax files to add to the list of languages micro can highlight.
|
||||
* How to create syntax files to add to the list of languages micro can
|
||||
highlight.
|
||||
|
||||
## Colorschemes
|
||||
|
||||
To change your colorscheme, press Ctrl-e in micro to bring up the command
|
||||
To change your colorscheme, press CtrlE in micro to bring up the command
|
||||
prompt, and type:
|
||||
|
||||
```
|
||||
@@ -24,32 +25,34 @@ colors can often be configured in the terminal preferences), and additional
|
||||
color support comes in three flavors.
|
||||
|
||||
* 16-color: A colorscheme that uses the 16 default colors will always work but
|
||||
will only look good if the 16 default colors have been configured to the user's
|
||||
liking. Using a colorscheme that only uses the 16 colors from the terminal palette
|
||||
will also preserve the terminal's theme from other applications since the terminal
|
||||
will often use those same colors for other applications. Default colorschemes
|
||||
of this type include `simple` and `solarized`.
|
||||
will only look good if the 16 default colors have been configured to the
|
||||
user's liking. Using a colorscheme that only uses the 16 colors from the
|
||||
terminal palette will also preserve the terminal's theme from other
|
||||
applications since the terminal will often use those same colors for other
|
||||
applications. Default colorschemes of this type include `simple` and
|
||||
`solarized`.
|
||||
|
||||
* 256-color: Almost all terminals support displaying an additional 240 colors on
|
||||
top of the 16 user-configurable colors (creating 256 colors total). Colorschemes
|
||||
which use 256-color are portable because they will look the same regardless of
|
||||
the configured 16-color palette. However, the color range is fairly limited
|
||||
due to the small number of colors available. Default 256-color colorschemes
|
||||
include `monokai`, `twilight`, `zenburn`, `darcula` and more.
|
||||
* 256-color: Almost all terminals support displaying an additional 240 colors
|
||||
on top of the 16 user-configurable colors (creating 256 colors total).
|
||||
Colorschemes which use 256-color are portable because they will look the
|
||||
same regardless of the configured 16-color palette. However, the color
|
||||
range is fairly limited due to the small number of colors available.
|
||||
Default 256-color colorschemes include `monokai`, `twilight`, `zenburn`,
|
||||
`darcula` and more.
|
||||
|
||||
* true-color: Some terminals support displaying "true color" with 16 million
|
||||
colors using standard RGB values. This mode will be able to support displaying
|
||||
any colorscheme, but it should be noted that the user-configured 16-color palette
|
||||
is ignored when using true-color mode (this means the colors while using the
|
||||
terminal emulator will be slightly off). Not all terminals support true color
|
||||
but at this point most do. True color support in micro is off by default but
|
||||
can be enabled by setting the environment variable `MICRO_TRUECOLOR` to 1.
|
||||
In addition your terminal must support it (usually indicated by setting `$COLORTERM`
|
||||
to `truecolor`).
|
||||
True-color colorschemes in micro typically end with `-tc`, such as `solarized-tc`,
|
||||
`atom-dark-tc`, `material-tc`, etc... If true color is not enabled but a true
|
||||
color colorscheme is used, micro will do its best to approximate the colors
|
||||
to the available 256 colors.
|
||||
colors using standard RGB values. This mode will be able to support
|
||||
displaying any colorscheme, but it should be noted that the user-configured
|
||||
16-color palette is ignored when using true-color mode (this means the
|
||||
colors while using the terminal emulator will be slightly off). Not all
|
||||
terminals support true color but at this point most do. True color
|
||||
support in micro is off by default but can be enabled by setting the
|
||||
environment variable `MICRO_TRUECOLOR` to 1. In addition your terminal
|
||||
must support it (usually indicated by setting `$COLORTERM` to `truecolor`).
|
||||
True-color colorschemes in micro typically end with `-tc`, such as
|
||||
`solarized-tc`, `atom-dark-tc`, `material-tc`, etc... If true color is not
|
||||
enabled but a true color colorscheme is used, micro will do its best to
|
||||
approximate the colors to the available 256 colors.
|
||||
|
||||
Here is the list of colorschemes:
|
||||
|
||||
@@ -71,16 +74,18 @@ themes the most.
|
||||
These may vary widely based on the 16 colors selected for your terminal.
|
||||
|
||||
* `simple`
|
||||
* `solarized` (must have the solarized color palette in your terminal to use this colorscheme properly)
|
||||
* `solarized` (must have the solarized color palette in your terminal to use
|
||||
this colorscheme properly)
|
||||
* `cmc-16`
|
||||
* `cmc-paper`
|
||||
* `geany`
|
||||
|
||||
### True color
|
||||
|
||||
True color requires your terminal to support it. This means that the environment variable
|
||||
`COLORTERM` should have the value `truecolor`, `24bit`, or `24-bit`. In addition, to enable
|
||||
true color in micro, the environment variable `MICRO_TRUECOLOR` must be set to 1.
|
||||
True color requires your terminal to support it. This means that the
|
||||
environment variable `COLORTERM` should have the value `truecolor`, `24bit`,
|
||||
or `24-bit`. In addition, to enable true color in micro, the environment
|
||||
variable `MICRO_TRUECOLOR` must be set to 1.
|
||||
|
||||
* `solarized-tc`: this is the solarized colorscheme for true color.
|
||||
* `atom-dark-tc`: this colorscheme is based off of Atom's "dark" colorscheme.
|
||||
@@ -92,9 +97,11 @@ true color in micro, the environment variable `MICRO_TRUECOLOR` must be set to 1
|
||||
## Creating a Colorscheme
|
||||
|
||||
Micro's colorschemes are also extremely simple to create. The default ones can
|
||||
be found [here](https://github.com/zyedidia/micro/tree/master/runtime/colorschemes).
|
||||
be found
|
||||
[here](https://github.com/zyedidia/micro/tree/master/runtime/colorschemes).
|
||||
|
||||
Custom colorschemes should be placed in the `~/.config/micro/colorschemes` directory.
|
||||
Custom colorschemes should be placed in the `~/.config/micro/colorschemes`
|
||||
directory.
|
||||
|
||||
A number of custom directives are placed in a `.micro` file. Colorschemes are
|
||||
typically only 18-30 lines in total.
|
||||
@@ -193,9 +200,9 @@ safe and recommended to use subgroups in your custom syntax files.
|
||||
For example if `constant.string` is found in your colorscheme, micro will us
|
||||
that for highlighting strings. If it's not found, it will use constant instead.
|
||||
Micro tries to match the largest set of groups it can find in the colorscheme
|
||||
definitions, so if, for examle `constant.bool.true` is found then micro will use
|
||||
that. If `constant.bool.true` is not found but `constant.bool` is found micro
|
||||
will use `constant.bool`. If not, it uses `constant`.
|
||||
definitions, so if, for examle `constant.bool.true` is found then micro will
|
||||
use that. If `constant.bool.true` is not found but `constant.bool` is found
|
||||
micro will use `constant.bool`. If not, it uses `constant`.
|
||||
|
||||
Here's a list of subgroups used in micro's built-in syntax files.
|
||||
|
||||
@@ -228,9 +235,9 @@ languages.
|
||||
Micro's builtin syntax highlighting tries very hard to be sane, sensible and
|
||||
provide ample coverage of the meaningful elements of a language. Micro has
|
||||
syntax files built in for over 100 languages now! However, there may be
|
||||
situations where you find Micro's highlighting to be insufficient or not to your
|
||||
liking. The good news is that you can create your own syntax files, and place them
|
||||
in `~/.config/micro/syntax` and Micro will use those instead.
|
||||
situations where you find Micro's highlighting to be insufficient or not to
|
||||
your liking. The good news is that you can create your own syntax files, and
|
||||
place them in `~/.config/micro/syntax` and Micro will use those instead.
|
||||
|
||||
### Filetype definition
|
||||
|
||||
@@ -249,9 +256,9 @@ detect:
|
||||
filename: "\\.go$"
|
||||
```
|
||||
|
||||
Micro will match this regex against a given filename to detect the filetype. You
|
||||
may also provide an optional `header` regex that will check the first line of
|
||||
the file. For example:
|
||||
Micro will match this regex against a given filename to detect the filetype.
|
||||
You may also provide an optional `header` regex that will check the first line
|
||||
of the file. For example:
|
||||
|
||||
```
|
||||
detect:
|
||||
@@ -262,8 +269,8 @@ detect:
|
||||
### Syntax rules
|
||||
|
||||
Next you must provide the syntax highlighting rules. There are two types of
|
||||
rules: patterns and regions. A pattern is matched on a single line and usually a
|
||||
single word as well. A region highlights between two patterns over multiple
|
||||
rules: patterns and regions. A pattern is matched on a single line and usually
|
||||
a single word as well. A region highlights between two patterns over multiple
|
||||
lines and may have rules of its own inside the region.
|
||||
|
||||
Here are some example patterns in Go:
|
||||
@@ -346,8 +353,8 @@ worry about them.
|
||||
|
||||
Syntax file headers are files that contain only the filetype and the detection
|
||||
regular expressions for a given syntax file. They have a `.hdr` suffix and are
|
||||
used by default only for the pre-installed syntax files. Header files allow micro
|
||||
to parse the syntax files much faster when checking the filetype of a certain
|
||||
file. Custom syntax files may provide header files in `~/.config/micro/syntax` as
|
||||
well but it is not necessary (only do this if you have many (100+) custom syntax
|
||||
files and want to improve performance).
|
||||
used by default only for the pre-installed syntax files. Header files allow
|
||||
micro to parse the syntax files much faster when checking the filetype of a
|
||||
certain file. Custom syntax files may provide header files in
|
||||
`~/.config/micro/syntax` as well but it is not necessary (only do this if you
|
||||
have many (100+) custom syntax files and want to improve performance).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Command bar
|
||||
|
||||
The command bar is opened by pressing Ctrl-e. It is a single-line buffer,
|
||||
The command bar is opened by pressing CtrlE. It is a single-line buffer,
|
||||
meaning that all keybindings from a normal buffer are supported (as well
|
||||
as mouse and selection).
|
||||
|
||||
@@ -12,8 +12,8 @@ does not look up environment variables.
|
||||
|
||||
# Commands
|
||||
|
||||
Micro provides the following commands that can be executed at the command-bar by
|
||||
pressing `Ctrl-e` and entering the command. Arguments are placed in single
|
||||
Micro provides the following commands that can be executed at the command-bar
|
||||
by pressing `CtrlE` and entering the command. Arguments are placed in single
|
||||
quotes here but these are not necessary when entering the command in micro.
|
||||
|
||||
* `bind 'key' 'action'`: creates a keybinding from key to action. See the
|
||||
@@ -42,41 +42,48 @@ quotes here but these are not necessary when entering the command in micro.
|
||||
|
||||
See `replace` command for more information.
|
||||
|
||||
* `set 'option' 'value'`: sets the option to value. See the `options` help topic for
|
||||
a list of options you can set. This will modify your `settings.json` with the
|
||||
new value.
|
||||
* `set 'option' 'value'`: sets the option to value. See the `options` help
|
||||
topic for a list of options you can set. This will modify your
|
||||
`settings.json` with the new value.
|
||||
|
||||
* `setlocal 'option' 'value'`: sets the option to value locally (only in the current
|
||||
buffer). This will *not* modify `settings.json`.
|
||||
* `setlocal 'option' 'value'`: sets the option to value locally (only in the
|
||||
current buffer). This will *not* modify `settings.json`.
|
||||
|
||||
* `show 'option'`: shows the current value of the given option.
|
||||
|
||||
* `run 'sh-command'`: runs the given shell command in the background. The
|
||||
command's output will be displayed in one line when it finishes running.
|
||||
|
||||
* `vsplit 'filename'`: opens a vertical split with `filename`. If no filename is
|
||||
provided, a vertical split is opened with an empty buffer.
|
||||
* `vsplit 'filename'`: opens a vertical split with `filename`. If no filename
|
||||
is provided, a vertical split is opened with an empty buffer.
|
||||
|
||||
* `hsplit 'filename'`: same as `vsplit` but opens a horizontal split instead of a
|
||||
vertical split.
|
||||
* `hsplit 'filename'`: same as `vsplit` but opens a horizontal split instead
|
||||
of a vertical split.
|
||||
|
||||
* `tab 'filename'`: opens the given file in a new tab.
|
||||
|
||||
* `tabswitch 'tab'`: This command will switch to the specified tab. The `tab` can
|
||||
either be a tab number, or a name of a tab.
|
||||
* `tabswitch 'tab'`: This command will switch to the specified tab. The `tab`
|
||||
can either be a tab number, or a name of a tab.
|
||||
|
||||
* `textfilter 'sh-command'`: filters the current selection through a shell
|
||||
command as standard input and replaces the selection with the stdout of
|
||||
the shell command. For example, to sort a list of numbers, first select
|
||||
them, and then execute `> textfilter sort -n`.
|
||||
|
||||
* `textfilter 'sh-command'`: filters the current selection through a shell command
|
||||
as standard input and replaces the selection with the stdout of the shell command.
|
||||
For example, to sort a list of numbers, first select them, and then execute
|
||||
`> textfilter sort -n`.
|
||||
|
||||
* `log`: opens a log of all messages and debug statements.
|
||||
|
||||
* `plugin 'list'`: lists all installed plugins.
|
||||
* `plugin list`: lists all installed plugins.
|
||||
|
||||
* `plugin version 'pl'`: shows version for specified plugin.
|
||||
* `plugin install 'pl'`: install a plugin.
|
||||
|
||||
* `plugin info 'pl'`: shows additional info for specified plugin.
|
||||
* `plugin remove 'pl'`: remove a plugin.
|
||||
|
||||
* `plugin update 'pl'`: update a plugin (if no arguments are provided
|
||||
updates all plugins).
|
||||
|
||||
* `plugin search 'pl'`: search available plugins for a keyword.
|
||||
|
||||
* `plugin available`: show available plugins that can be installed.
|
||||
|
||||
* `reload`: reloads all runtime files.
|
||||
|
||||
@@ -99,10 +106,13 @@ quotes here but these are not necessary when entering the command in micro.
|
||||
* `showkey`: Show the action(s) bound to a given key. For example
|
||||
running `> showkey CtrlC` will display `Copy`.
|
||||
|
||||
* `term exec?`: Open a terminal emulator running the given executable. If no
|
||||
executable is given, this will open the default shell in the terminal
|
||||
emulator.
|
||||
|
||||
---
|
||||
|
||||
The following commands are provided by the default plugins:
|
||||
|
||||
* `lint`: Lint the current file for errors.
|
||||
|
||||
* `comment`: automatically comment or uncomment current selection or line.
|
||||
|
||||
@@ -17,22 +17,24 @@ can change it!
|
||||
|
||||
### Navigation
|
||||
|
||||
| Key | Description of function |
|
||||
|--------------------------- |------------------------------------------------------------------------------------------ |
|
||||
| Arrows | Move the cursor around |
|
||||
| Shift+arrows | Move and select text |
|
||||
| Home or Ctrl+LeftArrow | Move to the beginning of the current line |
|
||||
| End or Ctrl+RightArrow | Move to the end of the current line |
|
||||
| Alt+LeftArrow | Move cursor one word left |
|
||||
| Alt+RightArrow | Move cursor one word right |
|
||||
| Alt+{ | Move cursor to previous empty line, or beginning of document |
|
||||
| Alt+} | Move cursor to next empty line, or end of document |
|
||||
| PageUp | Move cursor up one page |
|
||||
| PageDown | Move cursor down one page |
|
||||
| Ctrl+Home or Ctrl+UpArrow | Move cursor to start of document |
|
||||
| Ctrl+End or Ctrl+DownArrow | Move cursor to end of document |
|
||||
| Ctrl+L | Jump to a line in the file (prompts with #) |
|
||||
| Ctrl+W | Cycle between splits in the current tab (use `> vsplit` or `> hsplit` to create a split) |
|
||||
| Key | Description of function |
|
||||
|---------------------------- |------------------------------------------------------------------------------------------ |
|
||||
| Arrows | Move the cursor around |
|
||||
| Shift+arrows | Move and select text |
|
||||
| Alt(Ctrl on Mac)+LeftArrow | Move to the beginning of the current line |
|
||||
| Alt(Ctrl on Mac)+RightArrow | Move to the end of the current line |
|
||||
| Home | Move to the beginning of the current line |
|
||||
| End | Move to the end of the current line |
|
||||
| Ctrl(Alt on Mac)+LeftArrow | Move cursor one word left |
|
||||
| Ctrl(Alt on Mac)+RightArrow | Move cursor one word right |
|
||||
| Alt+{ | Move cursor to previous empty line, or beginning of document |
|
||||
| Alt+} | Move cursor to next empty line, or end of document |
|
||||
| PageUp | Move cursor up one page |
|
||||
| PageDown | Move cursor down one page |
|
||||
| Ctrl+Home or Ctrl+UpArrow | Move cursor to start of document |
|
||||
| Ctrl+End or Ctrl+DownArrow | Move cursor to end of document |
|
||||
| Ctrl+L | Jump to a line in the file (prompts with #) |
|
||||
| Ctrl+W | Cycle between splits in the current tab (use `> vsplit` or `> hsplit` to create a split) |
|
||||
|
||||
### Tabs
|
||||
|
||||
@@ -62,10 +64,12 @@ can change it!
|
||||
|
||||
| Key | Description of function |
|
||||
|------------------------------------ |------------------------------------------ |
|
||||
| Alt+Shift+RightArrow | Select word right |
|
||||
| Alt+Shift+LeftArrow | Select word left |
|
||||
| Shift+Home or Ctrl+Shift+LeftArrow | Select to start of current line |
|
||||
| Shift+End or Ctrl+Shift+RightArrow | Select to end of current line |
|
||||
| Ctrl(Alt on Mac)+Shift+RightArrow | Select word right |
|
||||
| Ctrl(Alt on Mac)+Shift+LeftArrow | Select word left |
|
||||
| Alt(Ctrl on Mac)+Shift+LeftArrow | Select to start of current line |
|
||||
| Alt(Ctrl on Mac)+Shift+RightArrow | Select to end of current line |
|
||||
| Shift+Home | Select to start of current line |
|
||||
| Shift+End | Select to end of current line |
|
||||
| Ctrl+Shift+UpArrow | Select to start of file |
|
||||
| Ctrl+Shift+DownArrow | Select to end of file |
|
||||
| Ctrl+X | Cut selected text |
|
||||
@@ -92,6 +96,8 @@ can change it!
|
||||
| Key | Description of function |
|
||||
|------------------ |---------------------------------------------------------------------------------------------- |
|
||||
| Alt+N | Create new multiple cursor from selection (will select current word if no current selection) |
|
||||
| AltShiftUp | Spawn a new cursor on the line above the current one |
|
||||
| AltShiftDown | Spawn a new cursor on the line below the current one |
|
||||
| Alt+P | Remove latest multiple cursor |
|
||||
| Alt+C | Remove all multiple cursors (cancel) |
|
||||
| Alt+X | Skip multiple cursor selection |
|
||||
|
||||
@@ -1,29 +1,31 @@
|
||||
# Micro help text
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
To open the command bar, press Ctrl-e. This enables a `>` prompt for typing
|
||||
commands. From now on when the documentation says to run a command such as
|
||||
`> help`, this means press Ctrl-e and type `help` (and press enter to execute
|
||||
the command).
|
||||
To open the command bar, press CtrlE. This enables a `>` prompt for typing
|
||||
commands. From now on when the documentation says to run a command such as `>
|
||||
help`, this means press CtrlE and type `help` (and press enter to execute the
|
||||
command).
|
||||
|
||||
For a list of the default keybindings run `> help defaultkeys`.
|
||||
For more information on keybindings see `> help keybindings`.
|
||||
|
||||
## Quick-start
|
||||
|
||||
Press Ctrl-q to quit, and Ctrl-s to save. Press Ctrl-e to start typing commands and
|
||||
you can see which commands are available by pressing tab, or by viewing the help
|
||||
topic `> help commands`.
|
||||
Press Ctrl-q to quit, and Ctrl-s 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`.
|
||||
|
||||
Move the cursor around with the mouse or the arrow keys. Run
|
||||
`> help defaultkeys` to get a quick, easy overview of the default hotkeys and
|
||||
what they do. For more info on rebinding keys, see type `> help keybindings`.
|
||||
|
||||
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 about colorschemes and syntax highlighting with `> help colors`.
|
||||
`> set colorscheme ...`. You can press tab to see the available colorschemes,
|
||||
or see more information about colorschemes and syntax highlighting with `> help
|
||||
colors`.
|
||||
|
||||
Press Ctrl-w to move between splits, and type `> vsplit filename` or
|
||||
`> hsplit filename` to open a new split.
|
||||
@@ -32,7 +34,7 @@ Press Ctrl-w to move between splits, and type `> vsplit filename` or
|
||||
|
||||
Micro has a built-in help system which can be accessed with the `help` command.
|
||||
|
||||
To use it, press Ctrl-e to access command mode and type in `help` followed by a
|
||||
To use it, press CtrlE to access command mode and type in `help` followed by a
|
||||
topic. Typing `help` followed by nothing will open this page.
|
||||
|
||||
Here are the possible help topics that you can read:
|
||||
@@ -41,14 +43,14 @@ Here are the possible help topics that you can read:
|
||||
topics
|
||||
* keybindings: Gives a full list of the default keybindings as well as how to
|
||||
rebind them
|
||||
* defaultkeys: Gives a more straight-forward list of the hotkey commands and what
|
||||
they do.
|
||||
* defaultkeys: Gives a more straight-forward list of the hotkey commands and
|
||||
what they do.
|
||||
* commands: Gives a list of all the commands and what they do
|
||||
* options: Gives a list of all the options you can customize
|
||||
* plugins: Explains how micro's plugin system works and how to create your own
|
||||
plugins
|
||||
* colors: Explains micro's colorscheme and syntax highlighting engine and how to
|
||||
create your own colorschemes or add new languages to the engine
|
||||
* colors: Explains micro's colorscheme and syntax highlighting engine and how
|
||||
to create your own colorschemes or add new languages to the engine
|
||||
|
||||
For example, to open the help page on plugins you would run `> help plugins`.
|
||||
|
||||
|
||||
@@ -14,16 +14,16 @@ at the end of this document).
|
||||
If `~/.config/micro/bindings.json` does not exist, you can simply create it.
|
||||
Micro will know what to do with it.
|
||||
|
||||
You can use the alt keys + arrows to move word by word. Ctrl left and right move
|
||||
the cursor to the start and end of the line, and ctrl up and down move the
|
||||
You can use the alt keys + arrows to move word by word. Ctrl left and right
|
||||
move the cursor to the start and end of the line, and ctrl up and down move the
|
||||
cursor the start and end of the buffer.
|
||||
|
||||
You can hold shift with all of these movement actions to select while moving.
|
||||
|
||||
## Rebinding keys
|
||||
|
||||
The bindings may be rebound using the `~/.config/micro/bindings.json` file. Each
|
||||
key is bound to an action.
|
||||
The bindings may be rebound using the `~/.config/micro/bindings.json` file.
|
||||
Each key is bound to an action.
|
||||
|
||||
For example, to bind `Ctrl-y` to undo and `Ctrl-z` to redo, you could put the
|
||||
following in the `bindings.json` file.
|
||||
@@ -58,9 +58,9 @@ bindings, tab is bound as
|
||||
"Tab": "Autocomplete|IndentSelection|InsertTab"
|
||||
```
|
||||
|
||||
This means that if the `Autocomplete` action is successful, the chain will abort.
|
||||
Otherwise, it will try `IndentSelection`, and if that fails too, it will
|
||||
execute `InsertTab`.
|
||||
This means that if the `Autocomplete` action is successful, the chain will
|
||||
abort. Otherwise, it will try `IndentSelection`, and if that fails too, it
|
||||
will execute `InsertTab`.
|
||||
|
||||
## Binding commands
|
||||
|
||||
@@ -87,8 +87,9 @@ you could rebind `CtrlG` to `> help`:
|
||||
}
|
||||
```
|
||||
|
||||
Now when you press `CtrlG`, `help` will appear in the command bar and your cursor will
|
||||
be placed after it (note the space in the json that controls the cursor placement).
|
||||
Now when you press `CtrlG`, `help` will appear in the command bar and your
|
||||
cursor will be placed after it (note the space in the json that controls the
|
||||
cursor placement).
|
||||
|
||||
## Binding raw escape sequences
|
||||
|
||||
@@ -103,15 +104,15 @@ starting with `0x1b`.
|
||||
For example, if micro reads `\x1b[1;5D`, on most terminals this will mean the
|
||||
user pressed CtrlLeft.
|
||||
|
||||
For many key chords though, the terminal won't send any escape code or will send
|
||||
an escape code already in use. For example for `CtrlBackspace`, my terminal
|
||||
sends `\u007f` (note this doesn't start with `0x1b`), which it also sends for
|
||||
`Backspace` meaning micro can't bind `CtrlBackspace`.
|
||||
For many key chords though, the terminal won't send any escape code or will
|
||||
send an escape code already in use. For example for `CtrlBackspace`, my
|
||||
terminal sends `\u007f` (note this doesn't start with `0x1b`), which it also
|
||||
sends for `Backspace` meaning micro can't bind `CtrlBackspace`.
|
||||
|
||||
However, some terminals do allow you to bind keys to send specific escape
|
||||
sequences you define. Then from micro you can directly bind those escape
|
||||
sequences to actions. For example, to bind `CtrlBackspace` you can instruct your
|
||||
terminal to send `\x1bctrlback` and then bind it in `bindings.json`:
|
||||
sequences to actions. For example, to bind `CtrlBackspace` you can instruct
|
||||
your terminal to send `\x1bctrlback` and then bind it in `bindings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -123,9 +124,9 @@ Here are some instructions for sending raw escapes in different terminals
|
||||
|
||||
### iTerm2
|
||||
|
||||
In iTerm2, you can do this in `Preferences->Profiles->Keys` then click the `+`,
|
||||
input your keybinding, and for the `Action` select `Send Escape Sequence`. For
|
||||
the above example your would type `ctrlback` into the box (the `\x1b`) is
|
||||
In iTerm2, you can do this in `Preferences->Profiles->Keys` then click the
|
||||
`+`, input your keybinding, and for the `Action` select `Send Escape Sequence`.
|
||||
For the above example your would type `ctrlback` into the box (the `\x1b`) is
|
||||
automatically sent by iTerm2.
|
||||
|
||||
### Linux using loadkeys
|
||||
@@ -230,6 +231,8 @@ Suspend (Unix only)
|
||||
ScrollUp
|
||||
ScrollDown
|
||||
SpawnMultiCursor
|
||||
SpawnMultiCursorUp
|
||||
SpawnMultiCursorDown
|
||||
SpawnMultiCursorSelect
|
||||
RemoveMultiCursor
|
||||
RemoveAllMultiCursors
|
||||
@@ -390,6 +393,10 @@ MouseWheelRight
|
||||
|
||||
# Default keybinding configuration.
|
||||
|
||||
A select few keybindings are different on MacOS compared to other
|
||||
operating systems. This is because different OSes have different
|
||||
conventions for text editing defaults.
|
||||
|
||||
```json
|
||||
{
|
||||
"Up": "CursorUp",
|
||||
@@ -404,13 +411,19 @@ MouseWheelRight
|
||||
"AltRight": "WordRight",
|
||||
"AltUp": "MoveLinesUp",
|
||||
"AltDown": "MoveLinesDown",
|
||||
"AltShiftRight": "SelectWordRight",
|
||||
"AltShiftLeft": "SelectWordLeft",
|
||||
"CtrlLeft": "StartOfLine",
|
||||
"CtrlRight": "EndOfLine",
|
||||
"CtrlShiftLeft": "SelectToStartOfLine",
|
||||
"CtrlShiftRight": "SelectWordRight",
|
||||
"CtrlShiftLeft": "SelectWordLeft",
|
||||
"AltLeft": "StartOfLine",
|
||||
"AltRight": "EndOfLine",
|
||||
"AltShiftRight": "SelectWordRight", (Mac)
|
||||
"AltShiftLeft": "SelectWordLeft", (Mac)
|
||||
"CtrlLeft": "StartOfText", (Mac)
|
||||
"CtrlRight": "EndOfLine", (Mac)
|
||||
"AltShiftLeft": "SelectToStartOfLine",
|
||||
"CtrlShiftLeft": "SelectToStartOfText", (Mac)
|
||||
"ShiftHome": "SelectToStartOfLine",
|
||||
"CtrlShiftRight": "SelectToEndOfLine",
|
||||
"AltShiftRight": "SelectToEndOfLine",
|
||||
"CtrlShiftRight": "SelectToEndOfLine", (Mac)
|
||||
"ShiftEnd": "SelectToEndOfLine",
|
||||
"CtrlUp": "CursorStart",
|
||||
"CtrlDown": "CursorEnd",
|
||||
@@ -483,11 +496,13 @@ MouseWheelRight
|
||||
"MouseMiddle": "PastePrimary",
|
||||
"Ctrl-MouseLeft": "MouseMultiCursor",
|
||||
|
||||
"Alt-n": "SpawnMultiCursor",
|
||||
"Alt-m": "SpawnMultiCursorSelect",
|
||||
"Alt-p": "RemoveMultiCursor",
|
||||
"Alt-c": "RemoveAllMultiCursors",
|
||||
"Alt-x": "SkipMultiCursor"
|
||||
"Alt-n": "SpawnMultiCursor",
|
||||
"AltShiftUp": "SpawnMultiCursorUp",
|
||||
"AltShiftDown": "SpawnMultiCursorDown",
|
||||
"Alt-m": "SpawnMultiCursorSelect",
|
||||
"Alt-p": "RemoveMultiCursor",
|
||||
"Alt-c": "RemoveAllMultiCursors",
|
||||
"Alt-x": "SkipMultiCursor",
|
||||
}
|
||||
```
|
||||
|
||||
@@ -502,8 +517,8 @@ Additionally, alt keys can be bound by using `Alt-key`. For example `Alt-a` or
|
||||
This is why in the default keybindings you can see `AltShiftLeft` instead of
|
||||
`Alt-ShiftLeft` (they are equivalent).
|
||||
|
||||
Please note that terminal emulators are strange applications and micro only receives
|
||||
key events that the terminal decides to send. Some terminal emulators may not
|
||||
send certain events even if this document says micro can receive the event. To see
|
||||
exactly what micro receives from the terminal when you press a key, run the `> raw`
|
||||
command.
|
||||
Please note that terminal emulators are strange applications and micro only
|
||||
receives key events that the terminal decides to send. Some terminal emulators
|
||||
may not send certain events even if this document says micro can receive the
|
||||
event. To see exactly what micro receives from the terminal when you press a
|
||||
key, run the `> raw` command.
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
Micro stores all of the user configuration in its configuration directory.
|
||||
|
||||
Micro uses `$MICRO_CONFIG_HOME` as the configuration directory. If this environment
|
||||
variable is not set, it uses `$XDG_CONFIG_HOME/micro` instead. If that
|
||||
environment variable is not set, it uses `~/.config/micro` as the configuration
|
||||
directory. In the documentation, we use `~/.config/micro` to refer to the
|
||||
configuration directory (even if it may in fact be somewhere else if you have
|
||||
set either of the above environment variables).
|
||||
Micro uses `$MICRO_CONFIG_HOME` as the configuration directory. If this
|
||||
environment variable is not set, it uses `$XDG_CONFIG_HOME/micro` instead. If
|
||||
that environment variable is not set, it uses `~/.config/micro` as the
|
||||
configuration directory. In the documentation, we use `~/.config/micro` to
|
||||
refer to the configuration directory (even if it may in fact be somewhere else
|
||||
if you have set either of the above environment variables).
|
||||
|
||||
Here are the available options:
|
||||
|
||||
@@ -19,11 +19,12 @@ Here are the available options:
|
||||
* `backup`: micro will automatically keep backups of all open buffers. Backups
|
||||
are stored in `~/.config/micro/backups` and are removed when the buffer is
|
||||
closed cleanly. In the case of a system crash or a micro crash, the contents
|
||||
of the buffer can be recovered automatically by opening the file that
|
||||
was being edited before the crash, or manually by searching for the backup
|
||||
in the backup directory. Backups are made in the background when a buffer is
|
||||
of the buffer can be recovered automatically by opening the file that was
|
||||
being edited before the crash, or manually by searching for the backup in
|
||||
the backup directory. Backups are made in the background when a buffer is
|
||||
modified and the latest backup is more than 8 seconds old, or when micro
|
||||
detects a crash. It is highly recommended that you leave this feature enabled.
|
||||
detects a crash. It is highly recommended that you leave this feature
|
||||
enabled.
|
||||
|
||||
default value: `true`
|
||||
|
||||
@@ -43,8 +44,9 @@ Here are the available options:
|
||||
|
||||
default value: `default`
|
||||
|
||||
Note that the default colorschemes (default, solarized, and solarized-tc)
|
||||
are not located in configDir, because they are embedded in the micro binary.
|
||||
Note that the default colorschemes (default, solarized, and solarized-tc)
|
||||
are not located in configDir, because they are embedded in the micro
|
||||
binary.
|
||||
|
||||
The colorscheme can be selected from all the files in the
|
||||
~/.config/micro/colorschemes/ directory. Micro comes by default with three
|
||||
@@ -67,29 +69,29 @@ Here are the available options:
|
||||
|
||||
default value: `false`
|
||||
|
||||
* `fastdirty`: this determines what kind of algorithm micro uses to determine if
|
||||
a buffer is modified or not. When `fastdirty` is on, micro just uses a
|
||||
* `fastdirty`: this determines what kind of algorithm micro uses to determine
|
||||
if a buffer is modified or not. When `fastdirty` is on, micro just uses a
|
||||
boolean `modified` that is set to `true` as soon as the user makes an edit.
|
||||
This is fast, but can be inaccurate. If `fastdirty` is off, then micro will
|
||||
hash the current buffer against a hash of the original file (created when the
|
||||
buffer was loaded). This is more accurate but obviously more resource
|
||||
hash the current buffer against a hash of the original file (created when
|
||||
the buffer was loaded). This is more accurate but obviously more resource
|
||||
intensive. This option is only for people who really care about having
|
||||
accurate modified status.
|
||||
|
||||
default value: `true`
|
||||
|
||||
* `fileformat`: this determines what kind of line endings micro will use for the
|
||||
file. UNIX line endings are just `\n` (linefeed) whereas dos line endings are
|
||||
`\r\n` (carriage return + linefeed). The two possible values for this option
|
||||
are `unix` and `dos`. The fileformat will be automatically detected (when you
|
||||
open an existing file) and displayed on the statusline, but this option is
|
||||
useful if you would like to change the line endings or if you are starting a
|
||||
new file.
|
||||
* `fileformat`: this determines what kind of line endings micro will use for
|
||||
the file. UNIX line endings are just `\n` (linefeed) whereas dos line
|
||||
endings are `\r\n` (carriage return + linefeed). The two possible values for
|
||||
this option are `unix` and `dos`. The fileformat will be automatically
|
||||
detected (when you open an existing file) and displayed on the statusline,
|
||||
but this option is useful if you would like to change the line endings or if
|
||||
you are starting a new file.
|
||||
|
||||
default value: `unix`
|
||||
|
||||
* `filetype`: sets the filetype for the current buffer. Set this option to `off`
|
||||
to completely disable filetype detection.
|
||||
* `filetype`: sets the filetype for the current buffer. Set this option to
|
||||
`off` to completely disable filetype detection.
|
||||
|
||||
default value: `unknown`. This will be automatically overridden depending
|
||||
on the file you open.
|
||||
@@ -109,8 +111,9 @@ Here are the available options:
|
||||
|
||||
* `keepautoindent`: when using autoindent, whitespace is added for you. This
|
||||
option determines if when you move to the next line without any insertions
|
||||
the whitespace that was added should be deleted to remove trailing whitespace.
|
||||
By default, the autoindent whitespace is deleted if the line was left empty.
|
||||
the whitespace that was added should be deleted to remove trailing
|
||||
whitespace. By default, the autoindent whitespace is deleted if the line
|
||||
was left empty.
|
||||
|
||||
default value: `false`
|
||||
|
||||
@@ -125,9 +128,9 @@ Here are the available options:
|
||||
|
||||
default value: `true`
|
||||
|
||||
* `mkparents`: if a file is opened on a path that does not exist, the file cannot
|
||||
be saved because the parent directories don't exist. This option lets micro
|
||||
automatically create the parent directories in such a situation.
|
||||
* `mkparents`: if a file is opened on a path that does not exist, the file
|
||||
cannot be saved because the parent directories don't exist. This option lets
|
||||
micro automatically create the parent directories in such a situation.
|
||||
|
||||
default value: `false`
|
||||
|
||||
@@ -141,10 +144,10 @@ Here are the available options:
|
||||
|
||||
* `paste`: Treat characters sent from the terminal in a single chunk as a paste
|
||||
event rather than a series of manual key presses. If you are pasting using
|
||||
the terminal keybinding (not Ctrl-v, which is micro's default paste keybinding)
|
||||
then it is a good idea to enable this option during the paste and disable
|
||||
once the paste is over. See `> help copypaste` for details about copying
|
||||
and pasting in a terminal environment.
|
||||
the terminal keybinding (not Ctrl-v, which is micro's default paste
|
||||
keybinding) then it is a good idea to enable this option during the paste
|
||||
and disable once the paste is over. See `> help copypaste` for details about
|
||||
copying and pasting in a terminal environment.
|
||||
|
||||
default value: `false`
|
||||
|
||||
@@ -218,8 +221,8 @@ Here are the available options:
|
||||
The `opt` and `bind` directives take either an option or an action afterward
|
||||
and fill in the value of the option or the key bound to the action.
|
||||
|
||||
default value: `$(filename) $(modified)($(line),$(col)) $(opt:filetype)
|
||||
$(opt:fileformat) $(opt:encoding)`
|
||||
default value: `$(filename) $(modified)($(line),$(col)) $(status.paste)|
|
||||
ft:$(opt:filetype) | $(opt:fileformat) | $(opt:encoding)`
|
||||
|
||||
* `statusformatr`: format string definition for the right-justified part of the
|
||||
statusline.
|
||||
@@ -262,8 +265,8 @@ Here are the available options:
|
||||
|
||||
---
|
||||
|
||||
Plugin options: all plugins come with a special option to enable or disable them. The option
|
||||
is a boolean with the same name as the plugin itself.
|
||||
Plugin options: all plugins come with a special option to enable or disable
|
||||
them. The option is a boolean with the same name as the plugin itself.
|
||||
|
||||
Any option you set in the editor will be saved to the file
|
||||
~/.config/micro/settings.json so, in effect, your configuration file will be
|
||||
@@ -281,9 +284,9 @@ locally rather than globally.
|
||||
The `colorscheme` option is global only, and the `filetype` option is local
|
||||
only. To set an option locally, use `setlocal` instead of `set`.
|
||||
|
||||
In the `settings.json` file you can also put set options locally by specifying either
|
||||
a glob or a filetype. Here is an example which has `tabstospaces` on for all files except Go
|
||||
files, and `tabsize` 4 for all files except Ruby files:
|
||||
In the `settings.json` file you can also put set options locally by specifying
|
||||
either a glob or a filetype. Here is an example which has `tabstospaces` on for
|
||||
all files except Go files, and `tabsize` 4 for all files except Ruby files:
|
||||
|
||||
```json
|
||||
{
|
||||
|
||||
@@ -97,8 +97,8 @@ function onSave(bp)
|
||||
end
|
||||
```
|
||||
|
||||
The `bp` variable is a reference to the bufpane the action is being executed within.
|
||||
This is almost always the current bufpane.
|
||||
The `bp` variable is a reference to the bufpane the action is being executed
|
||||
within. This is almost always the current bufpane.
|
||||
|
||||
All available actions are listed in the keybindings section of the help.
|
||||
|
||||
@@ -112,8 +112,8 @@ function onMousePress(view, event)
|
||||
end
|
||||
```
|
||||
|
||||
These functions should also return a boolean specifying whether the bufpane should
|
||||
be relocated to the cursor or not after the action is complete.
|
||||
These functions should also return a boolean specifying whether the bufpane
|
||||
should be relocated to the cursor or not after the action is complete.
|
||||
|
||||
## Accessing micro functions
|
||||
|
||||
@@ -129,76 +129,205 @@ micro.Log("Hello")
|
||||
The packages and functions are listed below (in Go type signatures):
|
||||
|
||||
* `micro`
|
||||
- `TermMessage(msg interface{}...)`
|
||||
- `TermError()`
|
||||
- `InfoBar()`
|
||||
- `Log(msg interface{}...)`
|
||||
- `SetStatusInfoFn(fn string)`
|
||||
* `micro/config`
|
||||
- `MakeCommand`
|
||||
- `FileComplete`
|
||||
- `HelpComplete`
|
||||
- `OptionComplete`
|
||||
- `OptionValueComplete`
|
||||
- `NoComplete`
|
||||
- `TryBindKey`
|
||||
- `Reload`
|
||||
- `AddRuntimeFilesFromDirectory`
|
||||
- `AddRuntimeFileFromMemory`
|
||||
- `AddRuntimeFile`
|
||||
- `ListRuntimeFiles`
|
||||
- `ReadRuntimeFile`
|
||||
- `RTColorscheme`
|
||||
- `RTSyntax`
|
||||
- `RTHelp`
|
||||
- `RTPlugin`
|
||||
- `RegisterCommonOption`
|
||||
- `RegisterGlobalOption`
|
||||
* `micro/shell`
|
||||
- `ExecCommand`
|
||||
- `RunCommand`
|
||||
- `RunBackgroundShell`
|
||||
- `RunInteractiveShell`
|
||||
- `JobStart`
|
||||
- `JobSpawn`
|
||||
- `JobStop`
|
||||
- `JobStop`
|
||||
- `RunTermEmulator`
|
||||
- `TermEmuSupported`
|
||||
* `micro/buffer`
|
||||
- `NewMessage`
|
||||
- `NewMessageAtLine`
|
||||
- `MTInfo`
|
||||
- `MTWarning`
|
||||
- `MTError`
|
||||
- `Loc`
|
||||
- `BTDefault`
|
||||
- `BTLog`
|
||||
- `BTRaw`
|
||||
- `BTInfo`
|
||||
- `NewBufferFromFile`
|
||||
- `ByteOffset`
|
||||
- `Log`
|
||||
- `LogBuf`
|
||||
* `micro/util`
|
||||
- `RuneAt`
|
||||
- `GetLeadingWhitespace`
|
||||
- `IsWordChar`
|
||||
- `TermMessage(msg interface{}...)`: temporarily close micro and print a
|
||||
message
|
||||
|
||||
- `TermError(filename string, lineNum int, err string)`: temporarily close
|
||||
micro and print an error formatted as `filename, lineNum: err`.
|
||||
|
||||
- `InfoBar()`: return the infobar BufPane object.
|
||||
|
||||
- `Log(msg interface{}...)`: write a message to `log.txt` (requires
|
||||
`-debug` flag, or binary built with `build-dbg`).
|
||||
|
||||
- `SetStatusInfoFn(fn string)`: register the given lua function as
|
||||
accessible from the statusline formatting options
|
||||
* `micro/config`
|
||||
- `MakeCommand(name string, action func(bp *BufPane, args[]string),
|
||||
completer buffer.Completer)`:
|
||||
create a command with the given name, and lua callback function when
|
||||
the command is run. A completer may also be given to specify how
|
||||
autocompletion should work with the custom command.
|
||||
|
||||
- `FileComplete`: autocomplete using files in the current directory
|
||||
- `HelpComplete`: autocomplete using names of help documents
|
||||
- `OptionComplete`: autocomplete using names of options
|
||||
- `OptionValueComplete`: autocomplete using names of options, and valid
|
||||
values afterwards
|
||||
- `NoComplete`: no autocompletion suggestions
|
||||
|
||||
- `TryBindKey(k, v string, overwrite bool) (bool, error)`: bind the key
|
||||
`k` to the string `v` in the `bindings.json` file. If `overwrite` is
|
||||
true, this will overwrite any existing binding to key `k`. Returns true
|
||||
if the binding was made, and a possible error (for example writing to
|
||||
`bindings.json` can cause an error).
|
||||
|
||||
- `Reload()`: reload configuration files.
|
||||
|
||||
- `AddRuntimeFileFromMemory(filetype RTFiletype, filename, data string)`:
|
||||
add a runtime file to the `filetype` runtime filetype, with name
|
||||
`filename` and data `data`.
|
||||
|
||||
- `AddRuntimeFilesFromDirectory(plugin string, filetype RTFiletype,
|
||||
directory, pattern string)`:
|
||||
add runtime files for the given plugin with the given RTFiletype from
|
||||
a directory within the plugin root. Only adds files that match the
|
||||
pattern using Go's `filepath.Match`
|
||||
|
||||
- `AddRuntimeFile(plugin string, filetype RTFiletype, filepath string)`:
|
||||
add a given file inside the plugin root directory as a runtime file
|
||||
to the given RTFiletype category.
|
||||
|
||||
- `ListRuntimeFiles(fileType RTFiletype) []string`: returns a list of
|
||||
names of runtime files of the given type.
|
||||
|
||||
- `ReadRuntimeFile(fileType RTFiletype, name string) string`: returns the
|
||||
contents of a given runtime file.
|
||||
|
||||
- `NewRTFiletype() int`: creates a new RTFiletype, and returns its value.
|
||||
|
||||
- `RTColorscheme`: runtime files for colorschemes.
|
||||
- `RTSyntax`: runtime files for syntax files.
|
||||
- `RTHelp`: runtime files for help documents.
|
||||
- `RTPlugin`: runtime files for plugin source code.
|
||||
|
||||
- `RegisterCommonOption(pl string, name string, defaultvalue interface{})`:
|
||||
registers a new option with for the given plugin. The name of the
|
||||
option will be `pl.name`, and will have the given default value. Since
|
||||
this registers a common option, the option will be modifiable on a
|
||||
per-buffer basis, while also having a global value (in the
|
||||
GlobalSettings map).
|
||||
|
||||
- `RegisterGlobalOption(pl string, name string, defaultvalue interface{})`:
|
||||
same as `RegisterCommonOption` but the option cannot be modified
|
||||
locally to each buffer.
|
||||
|
||||
- `GetGlobalOption(name string) interface{}`: returns the value of a
|
||||
given plugin in the `GlobalSettings` map.
|
||||
|
||||
- `SetGlobalOption(option, value string) error`: sets an option to a
|
||||
given value. Same as using the `> set` command. This will parse the
|
||||
value to the actual value type.
|
||||
|
||||
- `SetGlobalOptionNative(option string, value interface{}) error`: sets
|
||||
an option to a given value, where the type of value is the actual
|
||||
type of the value internally.
|
||||
* `micro/shell`
|
||||
- `ExecCommand(name string, arg ...string) (string, error)`: runs an
|
||||
executable with the given arguments, and pipes the output (stderr
|
||||
and stdout) of the executable to an internal buffer, which is
|
||||
returned as a string, along with a possible error.
|
||||
|
||||
- `RunCommand(input string) (string, error)`: same as `ExecCommand`,
|
||||
except this uses micro's argument parser to parse the arguments from
|
||||
the input. For example `cat 'hello world.txt' file.txt`, will pass
|
||||
two arguments in the `ExecCommand` argument list (quoting arguments
|
||||
will preserve spaces).
|
||||
|
||||
- `RunBackgroundShell(input string) (func() string, error)`: returns a
|
||||
function that will run the given shell command and return its output.
|
||||
|
||||
- `RunInteractiveShell(input string, wait bool, getOutput bool)
|
||||
(string, error)`:
|
||||
temporarily closes micro and runs the given command in the terminal.
|
||||
If `wait` is true, micro will wait for the user to press enter before
|
||||
returning to text editing. If `getOutput` is true, micro redirect
|
||||
stdout from the command to the returned string.
|
||||
|
||||
- `JobStart(cmd string, onStdout, onStderr,
|
||||
onExit func(string, []interface{}), userargs ...interface{})
|
||||
*exec.Cmd`:
|
||||
Starts a background job by running the shell on the given command
|
||||
(using `sh -c`). Three callbacks can be provided which will be called
|
||||
when the command generates stdout, stderr, or exits. The userargs will
|
||||
be passed to the callbacks, along with the output as the first
|
||||
argument of the callback.
|
||||
|
||||
- `JobSpawn(cmd string, cmdArgs []string, onStdout, onStderr,
|
||||
onExit func(string, []interface{}), userargs ...interface{})
|
||||
*exec.Cmd`:
|
||||
same as `JobStart`, except doesn't run the command through the shell
|
||||
and instead takes as inputs the list of arguments.
|
||||
|
||||
- `JobStop(cmd *exec.Cmd)`: kills a job.
|
||||
- `JobSend(cmd *exec.Cmd, data string)`: sends some data to a job's stdin.
|
||||
|
||||
- `RunTermEmulator(h *BufPane, input string, wait bool, getOutput bool,
|
||||
callback func(out string, userargs []interface{}),
|
||||
userargs []interface{}) error`:
|
||||
starts a terminal emulator from a given BufPane with the input command.
|
||||
If `wait` is true it will wait for the user to exit by pressing enter
|
||||
once the executable has terminated and if `getOutput` is true it will
|
||||
redirect the stdout of the process to a pipe which will be passed to
|
||||
the callback which is a function that takes a string and a list of
|
||||
optional user arguments. This function returns an error on systems
|
||||
where the terminal emulator is not supported.
|
||||
|
||||
- `TermEmuSupported`: true on systems where the terminal emulator is
|
||||
supported and false otherwise. Supported systems:
|
||||
* Linux
|
||||
* MacOS
|
||||
* Dragonfly
|
||||
* OpenBSD
|
||||
* FreeBSD
|
||||
|
||||
* `micro/buffer`
|
||||
- `NewMessage(owner string, msg string, start, end, Loc, kind MsgType)
|
||||
*Message`:
|
||||
creates a new message with an owner over a range given by the start
|
||||
and end locations.
|
||||
|
||||
- `NewMessageAtLine(owner string, msg string, line int, kindMsgType)
|
||||
*Message`:
|
||||
creates a new message with owner, type and message at a given line.
|
||||
|
||||
- `MTInfo`: info message.
|
||||
- `MTWarning`: warning message.
|
||||
- `MTError` error message.
|
||||
|
||||
- `Loc(x, y int) Loc`: creates a new location struct.
|
||||
|
||||
- `BTDefault`: default buffer type.
|
||||
- `BTLog`: log buffer type.
|
||||
- `BTRaw`: raw buffer type.
|
||||
- `BTInfo`: info buffer type.
|
||||
|
||||
- `NewBuffer(text, path string) *Buffer`: creates a new buffer with the
|
||||
given text at a certain path.
|
||||
|
||||
- `NewBufferFromFile(path string) (*Buffer, error)`: creates a new
|
||||
buffer by reading from disk at the given path.
|
||||
|
||||
- `ByteOffset(pos Loc, buf *Buffer) int`: returns the byte index of the
|
||||
given position in a buffer.
|
||||
|
||||
- `Log(s string)`: writes a string to the log buffer.
|
||||
- `LogBuf() *Buffer`: returns the log buffer.
|
||||
* `micro/util`
|
||||
- `RuneAt(str string, idx int) string`: returns the utf8 rune at a
|
||||
given index within a string.
|
||||
- `GetLeadingWhitespace(s string) string`: returns the leading
|
||||
whitespace of a string.
|
||||
- `IsWordChar(s string) bool`: returns true if the first rune in a
|
||||
string is a word character.
|
||||
- `String(b []byte) string`: converts a byte array to a string.
|
||||
- `RuneStr(r rune) string`: converts a rune to a string.
|
||||
|
||||
This may seem like a small list of available functions but some of the objects
|
||||
returned by the functions have many methods. The Lua plugin may access any
|
||||
public methods of an object returned by any of the functions above. Unfortunately
|
||||
it is not possible to list all the available functions on this page. Please
|
||||
go to the internal documentation at https://godoc.org/github.com/zyedidia/micro
|
||||
to see the full list of available methods. Note that only methods of types that
|
||||
are available to plugins via the functions above can be called from a plugin.
|
||||
For an even more detailed reference see the source code on Github.
|
||||
public methods of an object returned by any of the functions above.
|
||||
Unfortunately it is not possible to list all the available functions on this
|
||||
page. Please go to the internal documentation at
|
||||
https://godoc.org/github.com/zyedidia/micro to see the full list of available
|
||||
methods. Note that only methods of types that are available to plugins via
|
||||
the functions above can be called from a plugin. For an even more detailed
|
||||
reference see the source code on Github.
|
||||
|
||||
For example, with a BufPane object called `bp`, you could call the `Save` function
|
||||
in Lua with `bp:Save()`.
|
||||
For example, with a BufPane object called `bp`, you could call the `Save`
|
||||
function in Lua with `bp:Save()`.
|
||||
|
||||
Note that Lua uses the `:` syntax to call a function rather than Go's `.` syntax.
|
||||
Note that Lua uses the `:` syntax to call a function rather than Go's `.`
|
||||
syntax.
|
||||
|
||||
```go
|
||||
micro.InfoBar().Message()
|
||||
@@ -265,7 +394,8 @@ to plugins though it is rather small.
|
||||
|
||||
## Adding help files, syntax files, or colorschemes in your plugin
|
||||
|
||||
You can use the `AddRuntimeFile(name string, type config.RTFiletype, path string)`
|
||||
You can use the `AddRuntimeFile(name string, type config.RTFiletype,
|
||||
path string)`
|
||||
function to add various kinds of files to your plugin. For example, if you'd
|
||||
like to add a help topic to your plugin called `test`, you would create a
|
||||
`test.md` file, and call the function:
|
||||
@@ -295,17 +425,51 @@ There are 6 default plugins that come pre-installed with micro. These are
|
||||
* `status`: provides some extensions to the status line (integration with
|
||||
Git and more).
|
||||
|
||||
See `> help linter`, `> help comment`, and `> help status` for additional
|
||||
documentation specific to those plugins.
|
||||
|
||||
These are good examples for many use-cases if you are looking to write
|
||||
your own plugins.
|
||||
|
||||
## Plugin Manager
|
||||
|
||||
Micro's plugin manager is you! Ultimately the plugins that are created
|
||||
for micro are quite simple and don't require a complex automated tool
|
||||
to manage them. They should be "git cloned" or somehow placed in the
|
||||
`~/.config/micro/plug` directory, and that is all that's necessary
|
||||
for installation. In the rare case that a more complex installation
|
||||
process is needed (such as dependencies, or additional setup) the
|
||||
plugin creator should provide the additional instructions on their
|
||||
website and point to the link using the `install` field in the `info.json`
|
||||
file.
|
||||
Micro also has a built in plugin manager which you can invoke with the
|
||||
`> plugin ...` command, or in the shell with `micro -plugin ...`.
|
||||
|
||||
For the valid commands you can use, see the `command` help topic.
|
||||
|
||||
The manager fetches plugins from the channels (which is simply a list of plugin
|
||||
metadata) which it knows about. By default, micro only knows about the official
|
||||
channel which is located at github.com/micro-editor/plugin-channel but you can
|
||||
add your own third-party channels using the `pluginchannels` option and you can
|
||||
directly link third-party plugins to allow installation through the plugin
|
||||
manager with the `pluginrepos` option.
|
||||
|
||||
If you'd like to publish a plugin you've made as an official plugin, you should
|
||||
upload your plugin online (to Github preferably) and add a `repo.json` file.
|
||||
This file will contain the metadata for your plugin. Here is an example:
|
||||
|
||||
```json
|
||||
[{
|
||||
"Name": "pluginname",
|
||||
"Description": "Here is a nice concise description of my plugin",
|
||||
"Website": "https://github.com/user/plugin",
|
||||
"Tags": ["python", "linting"],
|
||||
"Versions": [
|
||||
{
|
||||
"Version": "1.0.0",
|
||||
"Url": "https://github.com/user/plugin/archive/v1.0.0.zip",
|
||||
"Require": {
|
||||
"micro": ">=1.0.3"
|
||||
}
|
||||
}
|
||||
]
|
||||
}]
|
||||
```
|
||||
|
||||
Then open a pull request at github.com/micro-editor/plugin-channel adding a
|
||||
link to the raw `repo.json` that is in your plugin repository.
|
||||
|
||||
To make updating the plugin work, the first line of your plugins lua code
|
||||
should contain the version of the plugin. (Like this: `VERSION = "1.0.0"`)
|
||||
Please make sure to use [semver](http://semver.org/) for versioning.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Tutorial
|
||||
|
||||
This is a brief intro to micro's configuration system that will give some simple
|
||||
examples showing how to configure settings, rebind keys, and use `init.lua` to
|
||||
configure micro to your liking.
|
||||
This is a brief intro to micro's configuration system that will give some
|
||||
simple examples showing how to configure settings, rebind keys, and use
|
||||
`init.lua` to configure micro to your liking.
|
||||
|
||||
Hopefully you'll find this useful.
|
||||
|
||||
@@ -21,8 +21,8 @@ future, I will use `> set option value` to indicate pressing CtrlE). The change
|
||||
will take effect immediately and will also be saved to the `settings.json` file
|
||||
so that the setting will stick even after you close micro.
|
||||
|
||||
You can also set options locally which means that the setting will only have the
|
||||
value you give it in the buffer you set it in. For example, if you have two
|
||||
You can also set options locally which means that the setting will only have
|
||||
the value you give it in the buffer you set it in. For example, if you have two
|
||||
splits open, and you type `> setlocal tabsize 2`, the tabsize will only be 2 in
|
||||
the current buffer. Also micro will not save this local change to the
|
||||
`settings.json` file. However, you can still set options locally in the
|
||||
@@ -60,20 +60,21 @@ following in `bindings.json`:
|
||||
Very simple.
|
||||
|
||||
You can also bind keys while in micro by using the `> bind key action` command,
|
||||
but the bindings you make with the command won't be saved to the `bindings.json`
|
||||
file.
|
||||
but the bindings you make with the command won't be saved to the
|
||||
`bindings.json` file.
|
||||
|
||||
For more information about keybindings, like which keys can be bound, and
|
||||
what actions are available, see the `keybindings` help topic (`> help keybindings`).
|
||||
For more information about keybindings, like which keys can be bound, and what
|
||||
actions are available, see the `keybindings` help topic (`> help keybindings`).
|
||||
|
||||
### Configuration with Lua
|
||||
|
||||
If you need more power than the json files provide, you can use the `init.lua`
|
||||
file. Create it in `~/.config/micro`. This file is a lua file that is run when
|
||||
micro starts and is essentially a one-file plugin. The plugin name is `initlua`.
|
||||
micro starts and is essentially a one-file plugin. The plugin name is
|
||||
`initlua`.
|
||||
|
||||
This example will show you how to use the `init.lua` file by creating
|
||||
a binding to `CtrlR` which will execute the bash command `go run` on the current file,
|
||||
This example will show you how to use the `init.lua` file by creating a binding
|
||||
to `CtrlR` which will execute the bash command `go run` on the current file,
|
||||
given that the current file is a Go file.
|
||||
|
||||
You can do that by putting the following in `init.lua`:
|
||||
@@ -98,8 +99,8 @@ function gorun(bp)
|
||||
end
|
||||
```
|
||||
|
||||
Alternatively, you could get rid of the `TryBindKey` line, and put this line in the
|
||||
`bindings.json` file:
|
||||
Alternatively, you could get rid of the `TryBindKey` line, and put this line in
|
||||
the `bindings.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
VERSION = "1.0.0"
|
||||
|
||||
local uutil = import("micro/util")
|
||||
local utf8 = import("utf8")
|
||||
local autoclosePairs = {"\"\"", "''", "``", "()", "{}", "[]"}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "autoclose",
|
||||
"description": "Automatically places closing characters for quotes, parentheses, brackets, etc...",
|
||||
"website": "https://github.com/zyedidia/micro",
|
||||
"install": "https://github.com/zyedidia/micro",
|
||||
"version": "1.0.0",
|
||||
"require": [
|
||||
"micro >= 2.0.0"
|
||||
]
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
VERSION = "1.0.0"
|
||||
|
||||
local util = import("micro/util")
|
||||
local config = import("micro/config")
|
||||
local buffer = import("micro/buffer")
|
||||
@@ -102,5 +104,8 @@ function string.starts(String,Start)
|
||||
return string.sub(String,1,string.len(Start))==Start
|
||||
end
|
||||
|
||||
config.MakeCommand("comment", "comment.comment", config.NoComplete)
|
||||
config.TryBindKey("Alt-/", "lua:comment.comment", false)
|
||||
function init()
|
||||
config.MakeCommand("comment", comment, config.NoComplete)
|
||||
config.TryBindKey("Alt-/", "lua:comment.comment", false)
|
||||
config.AddRuntimeFile("comment", config.RTHelp, "help/comment.md")
|
||||
end
|
||||
|
||||
60
runtime/plugins/comment/help/comment.md
Normal file
60
runtime/plugins/comment/help/comment.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Comment Plugin
|
||||
|
||||
The comment plugin provides auto commenting/uncommenting.
|
||||
The default binding to comment/uncomment a line is `Alt-/`,
|
||||
but you can easily modify that in your `bindings.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"Alt-g": "comment.comment"
|
||||
}
|
||||
```
|
||||
|
||||
You can also execute a command which will do the same thing as
|
||||
the binding:
|
||||
|
||||
```
|
||||
> comment
|
||||
```
|
||||
|
||||
If you have a selection, the plugin will comment all the lines
|
||||
selected.
|
||||
|
||||
The comment type will be auto detected based on the filetype,
|
||||
but it is only available for certain filetypes:
|
||||
|
||||
* c: `// %s`
|
||||
* c++: `// %s`
|
||||
* d: `// %s`
|
||||
* go: `// %s`
|
||||
* html: `<!-- %s -->`
|
||||
* java: `// %s`
|
||||
* javascript: `// %s`
|
||||
* julia: `# %s`
|
||||
* lua: `-- %s`
|
||||
* perl: `# %s`
|
||||
* php: `// %s`
|
||||
* python: `# %s`
|
||||
* python3: `# %s`
|
||||
* ruby: `# %s`
|
||||
* rust: `// %s`
|
||||
* shell: `# %s`
|
||||
* swift: `// %s`
|
||||
|
||||
If your filetype is not available here, you can simply modify
|
||||
the `commenttype` option:
|
||||
|
||||
```
|
||||
set commenttype "/* %s */"
|
||||
```
|
||||
|
||||
Or in your `settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"*.c": {
|
||||
"commenttype": "/* %s */"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "comment",
|
||||
"description": "Support for automatically commenting blocks of code. Extensible and multiple languages supported.",
|
||||
"website": "https://github.com/zyedidia/micro",
|
||||
"install": "https://github.com/zyedidia/micro",
|
||||
"version": "1.0.0",
|
||||
"require": [
|
||||
"micro >= 2.0.0"
|
||||
]
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
VERSION = "1.0.0"
|
||||
|
||||
function onBufferOpen(b)
|
||||
local ft = b:FileType()
|
||||
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "ftoptions",
|
||||
"description": "Sets basic options based on the filetype (for example Makefiles require tabs).",
|
||||
"website": "https://github.com/zyedidia/micro",
|
||||
"install": "https://github.com/zyedidia/micro",
|
||||
"version": "1.0.0",
|
||||
"require": [
|
||||
"micro >= 2.0.0"
|
||||
]
|
||||
}
|
||||
80
runtime/plugins/linter/help/linter.md
Normal file
80
runtime/plugins/linter/help/linter.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# Linter
|
||||
|
||||
The linter plugin runs a compiler or linter on your source code
|
||||
and parses the resulting output so that the messages and line numbers
|
||||
can be viewed from within micro. By default, the plugin supports the
|
||||
following filetypes and linters:
|
||||
|
||||
* c: gcc
|
||||
* c++: g++
|
||||
* d: dmd
|
||||
* go: go build
|
||||
* java: javac
|
||||
* javascript: jshint
|
||||
* literate: lit
|
||||
* lua: luacheck
|
||||
* nim: nim
|
||||
* objective-c: clang
|
||||
* python: pyflakes
|
||||
* python: mypy
|
||||
* python: pylint
|
||||
* shell: shfmt
|
||||
* swift: swiftc
|
||||
* yaml: yamllint
|
||||
|
||||
If the linter plugin is enabled and the file corresponds to one of
|
||||
these filetypes, each time the buffer is saved, or when the `> lint`
|
||||
command is executed, micro will run the corresponding utility in the
|
||||
background and display the messages when it completes.
|
||||
|
||||
The linter plugin also allows users to extend the supported filetypes.
|
||||
From inside another micro plugin, the function `linter.makeLinter` can
|
||||
be called to register a new filetype. Here is the spec for the `makeLinter`
|
||||
function:
|
||||
|
||||
* `linter.makeLinter(name, filetype, cmd, args, errorformat, os, whitelist, domatch, loffset, coffset, callback)`
|
||||
|
||||
> name: name of the linter
|
||||
> filetype: filetype to check for to use linter
|
||||
> cmd: main linter process that is executed
|
||||
> args: arguments to pass to the linter process
|
||||
use %f to refer to the current file name
|
||||
use %d to refer to the current directory name
|
||||
> errorformat: how to parse the linter/compiler process output
|
||||
%f: file, %l: line number, %m: error/warning message
|
||||
> os: list of OSs this linter is supported or unsupported on
|
||||
optional param, default: {}
|
||||
> whitelist: should the OS list be a blacklist (do not run the linter for these OSs)
|
||||
or a whitelist (only run the linter for these OSs)
|
||||
optional param, default: false (should blacklist)
|
||||
> domatch: should the filetype be interpreted as a lua pattern to match with
|
||||
the actual filetype, or should the linter only activate on an exact match
|
||||
optional param, default: false (require exact match)
|
||||
> loffset: line offset will be added to the line number returned by the linter
|
||||
useful if the linter returns 0-indexed lines
|
||||
optional param, default: 0
|
||||
> coffset: column offset will be added to the col number returned by the linter
|
||||
useful if the linter returns 0-indexed columns
|
||||
optional param, default: 0
|
||||
> callback: function to call before executing the linter, if it returns
|
||||
false the lint is canceled. The callback is passed the buf.
|
||||
optional param, default: nil
|
||||
|
||||
Below is an example for including a linter for any filetype using
|
||||
the `misspell` linter which checks for misspelled words in a file.
|
||||
|
||||
```lua
|
||||
local config = import("micro/config")
|
||||
|
||||
function init()
|
||||
-- uses the default linter plugin
|
||||
-- matches any filetype
|
||||
linter.makeLinter("misspell", "", "misspell", {"%f"}, "%f:%l:%c: %m", {}, false, true, 0, 0, hasMisspell)
|
||||
|
||||
config.RegisterCommonOption("misspell", true)
|
||||
end
|
||||
|
||||
function hasMisspell(buf)
|
||||
return buf.Settings["misspell"]
|
||||
end
|
||||
```
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "linter",
|
||||
"description": "Automatic code linting for a variety of languages.",
|
||||
"website": "https://github.com/zyedidia/micro",
|
||||
"install": "https://github.com/zyedidia/micro",
|
||||
"version": "1.0.0",
|
||||
"require": [
|
||||
"micro >= 2.0.0"
|
||||
]
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
VERSION = "1.0.0"
|
||||
|
||||
local micro = import("micro")
|
||||
local runtime = import("runtime")
|
||||
local filepath = import("path/filepath")
|
||||
@@ -32,7 +34,10 @@ local linters = {}
|
||||
-- coffset: column offset will be added to the col number returned by the linter
|
||||
-- useful if the linter returns 0-indexed columns
|
||||
-- optional param, default: 0
|
||||
function makeLinter(name, filetype, cmd, args, errorformat, os, whitelist, domatch, loffset, coffset)
|
||||
-- callback: function to call before executing the linter, if it returns
|
||||
-- false the lint is canceled. The callback is passed the buf.
|
||||
-- optional param, default: nil
|
||||
function makeLinter(name, filetype, cmd, args, errorformat, os, whitelist, domatch, loffset, coffset, callback)
|
||||
if linters[name] == nil then
|
||||
linters[name] = {}
|
||||
linters[name].filetype = filetype
|
||||
@@ -44,6 +49,7 @@ function makeLinter(name, filetype, cmd, args, errorformat, os, whitelist, domat
|
||||
linters[name].domatch = domatch or false
|
||||
linters[name].loffset = loffset or 0
|
||||
linters[name].coffset = coffset or 0
|
||||
linters[name].callback = callback or nil
|
||||
end
|
||||
end
|
||||
|
||||
@@ -76,12 +82,12 @@ function init()
|
||||
makeLinter("swiftc", "swiftc", {"%f"}, "%f:%l:%c:.+: %m", {"linux"}, true)
|
||||
makeLinter("yaml", "yaml", "yamllint", {"--format", "parsable", "%f"}, "%f:%l:%c:.+ %m")
|
||||
|
||||
config.MakeCommand("lint", "linter.lintCmd", config.NoComplete)
|
||||
end
|
||||
config.MakeCommand("lint", function(bp, args)
|
||||
bp:Save()
|
||||
runLinter(bp.Buf)
|
||||
end, config.NoComplete)
|
||||
|
||||
function lintCmd(bp, args)
|
||||
bp:Save()
|
||||
runLinter(bp.Buf)
|
||||
config.AddRuntimeFile("linter", config.RTHelp, "help/linter.md")
|
||||
end
|
||||
|
||||
function contains(list, element)
|
||||
@@ -118,7 +124,7 @@ function runLinter(buf)
|
||||
end
|
||||
|
||||
if ftmatch then
|
||||
lint(buf, k, v.cmd, args, v.errorformat, v.loffset, v.coffset)
|
||||
lint(buf, k, v.cmd, args, v.errorformat, v.loffset, v.coffset, v.callback)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -128,10 +134,16 @@ function onSave(bp)
|
||||
return true
|
||||
end
|
||||
|
||||
function lint(buf, linter, cmd, args, errorformat, loff, coff)
|
||||
function lint(buf, linter, cmd, args, errorformat, loff, coff, callback)
|
||||
buf:ClearMessages(linter)
|
||||
|
||||
shell.JobSpawn(cmd, args, "", "", "linter.onExit", buf, linter, errorformat, loff, coff)
|
||||
if callback ~= nil then
|
||||
if not callback(buf) then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
shell.JobSpawn(cmd, args, nil, nil, onExit, buf, linter, errorformat, loff, coff)
|
||||
end
|
||||
|
||||
function onExit(output, args)
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "literate",
|
||||
"description": "Highlighting and language support for the Literate programming tool.",
|
||||
"website": "https://github.com/zyedidia/Literate",
|
||||
"install": "https://github.com/zyedidia/micro",
|
||||
"version": "1.0.0",
|
||||
"require": [
|
||||
"micro >= 2.0.0"
|
||||
]
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
VERSION = "1.0.0"
|
||||
|
||||
local config = import("micro/config")
|
||||
|
||||
function startswith(str, start)
|
||||
|
||||
15
runtime/plugins/status/help/status.md
Normal file
15
runtime/plugins/status/help/status.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Status
|
||||
|
||||
The status plugin provides some functions for modifying the status line.
|
||||
|
||||
Using the `statusformatl` and `statusformatr` options, the exact contents
|
||||
of the status line can be modified. Please see the documentation for
|
||||
those options (`> help options`) for more information.
|
||||
|
||||
This plugin provides the three functions that can be used in the status
|
||||
line format:
|
||||
|
||||
* `status.branch`: returns the name of the current git branch.
|
||||
* `status.hash`: returns the hash of the current git commit.
|
||||
* `status.paste`: returns "" if the paste option is disabled and "PASTE"
|
||||
if it is enabled.
|
||||
@@ -1,11 +1,14 @@
|
||||
micro = import("micro")
|
||||
buffer = import("micro/buffer")
|
||||
config = import("micro/config")
|
||||
VERSION = "1.0.0"
|
||||
|
||||
local micro = import("micro")
|
||||
local buffer = import("micro/buffer")
|
||||
local config = import("micro/config")
|
||||
|
||||
function init()
|
||||
micro.SetStatusInfoFn("status.branch")
|
||||
micro.SetStatusInfoFn("status.hash")
|
||||
micro.SetStatusInfoFn("status.paste")
|
||||
config.AddRuntimeFile("status", config.RTHelp, "help/status.md")
|
||||
end
|
||||
|
||||
function branch(b)
|
||||
@@ -17,6 +20,7 @@ function branch(b)
|
||||
if err == nil then
|
||||
return strings.TrimSpace(branch)
|
||||
end
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
@@ -29,6 +33,7 @@ function hash(b)
|
||||
if err == nil then
|
||||
return strings.TrimSpace(hash)
|
||||
end
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ rules:
|
||||
# Annotation
|
||||
- identifier.var: "@[A-Za-z]+"
|
||||
|
||||
- identifier: "[A-Za-z_][A-Za-z0-9_]*[[:space:]]*[()]"
|
||||
- identifier: "([A-Za-z0-9_]*[[:space:]]*[()])"
|
||||
- type: "\\b(bool|byte|sbyte|char|decimal|double|float|IntPtr|int|uint|long|ulong|object|short|ushort|string|base|this|var|void)\\b"
|
||||
- statement: "\\b(alias|as|case|catch|checked|default|do|dynamic|else|finally|fixed|for|foreach|goto|if|is|lock|new|null|return|switch|throw|try|unchecked|while)\\b"
|
||||
- statement: "\\b(abstract|async|class|const|delegate|enum|event|explicit|extern|get|implicit|in|internal|interface|namespace|operator|out|override|params|partial|private|protected|public|readonly|ref|sealed|set|sizeof|stackalloc|static|struct|typeof|unsafe|using|value|virtual|volatile|yield)\\b"
|
||||
|
||||
34
runtime/syntax/forth.yaml
Normal file
34
runtime/syntax/forth.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
filetype: forth
|
||||
|
||||
detect:
|
||||
filename: "\\.(forth|4th|fs|fs8|ft|fth|frt)$"
|
||||
|
||||
rules:
|
||||
- identifier: "\\b[A-Za-z_0-9-]*\\b"
|
||||
|
||||
- statement: "\\b(?i:(if|else|then|do|loop|case|endcase|of|endof|begin|while|repeat|until|again|unloop|leave|exit|done|next|\\?do|\\+do|\\-do|\\+loop|\\-loop|\\?leave))\\b"
|
||||
|
||||
- statement: "(^:|;$)"
|
||||
|
||||
- type: "\\b(?i:(variable|constant|cells))\\b"
|
||||
|
||||
- special: "\\B[?.]\\B" #for some reason, \b and \B are inverted for symbols
|
||||
|
||||
- constant.number: "\\b[0-9]+\\b"
|
||||
|
||||
- constant.string:
|
||||
start: "\\b([Ss.]\" )"
|
||||
end: "\""
|
||||
rules: []
|
||||
|
||||
- comment:
|
||||
start: "\\("
|
||||
end: "\\)"
|
||||
rules:
|
||||
- todo: "(TODO|NOTE|XXX|FIXME):?"
|
||||
|
||||
- comment:
|
||||
start: "\\\\"
|
||||
end: "$"
|
||||
rules:
|
||||
- todo: "(TODO|NOTE|XXX|FIXME):?"
|
||||
@@ -4,7 +4,7 @@ detect:
|
||||
filename: "\\.(gql|graphql)$"
|
||||
|
||||
rules:
|
||||
- type: "\\b(?:(query|mutation|subscription|type|fragment|schema|union|on|extends?))\\b"
|
||||
- type: "\\b(?:(query|mutation|subscription|type|input|scalar|fragment|schema|union|on|extends?))\\b"
|
||||
|
||||
# scalar types
|
||||
- statement: "\\b(ID|Int|Float|Boolean|String|Datetime|Null)\\b"
|
||||
|
||||
@@ -22,7 +22,7 @@ rules:
|
||||
- statement: "\\b(get|if|import|from|in|of|instanceof|let|new|reject|resolve|return)\\b"
|
||||
- statement: "\\b(set|super|switch|this|throw|try|typeof|var|void|while|with|yield)\\b"
|
||||
# reserved but unassigned
|
||||
- error: "\\b(enum|implements|interface|package|private|protected|public|TODO)"
|
||||
- error: "\\b(enum|implements|interface|package|private|protected|public)"
|
||||
- constant: "\\b(globalThis|Infinity|null|undefined|NaN)\\b"
|
||||
- constant: "\\b(null|undefined|NaN)\\b"
|
||||
- constant: "\\b(true|false)\\b"
|
||||
@@ -60,13 +60,20 @@ rules:
|
||||
- constant.specialChar: "\\\\."
|
||||
- identifier: "\\x24\\{.*?\\}"
|
||||
|
||||
- constant.bool: "\\b(true|false)\\b"
|
||||
- constant.bool.false: "\\b(false)\\b"
|
||||
- constant.bool.true: "\\b(true)\\b"
|
||||
|
||||
- comment:
|
||||
start: "//"
|
||||
end: "$"
|
||||
rules: []
|
||||
rules:
|
||||
- todo: "(TODO|XXX|FIXME)"
|
||||
|
||||
- comment:
|
||||
start: "/\\*"
|
||||
end: "\\*/"
|
||||
rules: []
|
||||
|
||||
rules:
|
||||
# function documentation
|
||||
- identifier: "\\s\\*\\s.*"
|
||||
- todo: "(TODO|XXX|FIXME)"
|
||||
|
||||
92
runtime/syntax/jsonnet.yaml
Normal file
92
runtime/syntax/jsonnet.yaml
Normal file
@@ -0,0 +1,92 @@
|
||||
filename: jsonnet
|
||||
|
||||
detect:
|
||||
filename: "\\.jsonnet$"
|
||||
|
||||
# Spec: https://jsonnet.org/ref/spec.html
|
||||
|
||||
rules:
|
||||
# built-in objects
|
||||
# FIXME: $ won't match
|
||||
- constant: "\\b(self|\\$|super)\\b"
|
||||
# boolean constants
|
||||
- constant.bool: "\\b(null|true|false)\\b"
|
||||
# the standard library
|
||||
- identifier: "\\bstd\\.(extVar|thisFile|type|length|objectHas|objectFields|objectHasAll|objectFieldsAll|prune|mapWithKey|abs|sign|max|min|pow|exp|log|exponent|mantissa|floor|ceil|sqrt|sin|cos|tan|asin|acos|atan|mod|assertEqual|toString|codepoint|char|substr|findSubstr|startsWith|endsWith|split|splitLimit|strReplace|asciiUpper|asciiLower|stringChars|format|escapeStringDollars|escapeStringPython|parseInt|parseOctal|parseHex|parseJson|encodeUTF8|decodeUTF8|manifestIni|manifestPython|manifestPythonVars|manifestJsonEx|manifestYamlDoc|manifestYamlStream|manifestXmlJsonml|makeArray|count|find|map|mapWithIndex|filterMap|filter|foldl|foldr|range|join|lines|flattenArrays|sort|uniq|set|setInter|setUnion|setDiff|setMember|base64|base64DecodeBytes|base64Decode|md5|mergePatch|trace)\\b"
|
||||
# unquoted object keys
|
||||
- type: "[_a-zA-Z][_a-zA-Z0-9]*\\s*:"
|
||||
# object key separator
|
||||
- statement: ":"
|
||||
# keywords
|
||||
- statement: "\\b(assert|else|error|for|function|if|import|importstr|in|local|tailstrict|then)\\b"
|
||||
# operators
|
||||
- symbol.operator: "([.;,+*|=!\\%]|<|>|/|-|&)"
|
||||
# parentheses
|
||||
- symbol.brackets: "([(){}]|\\[|\\])"
|
||||
# numbers
|
||||
- constant.number: "\\b(0|([1-9][0-9]*))(\\.[0-9]+)?([eE][\\+-]?[0-9]+)?\\b"
|
||||
|
||||
# double-quoted string
|
||||
- constant.string:
|
||||
start: "\""
|
||||
end: "\""
|
||||
skip: "\\\\\""
|
||||
rules:
|
||||
- constant.specialChar: "\\\\u[0-9a-fA-F]{4}|\\\\[bfnrt'\"/\\\\]"
|
||||
|
||||
# single-quoted string
|
||||
- constant.string:
|
||||
start: "'"
|
||||
end: "'"
|
||||
skip: "\\\\'"
|
||||
rules:
|
||||
- constant.specialChar: "\\\\u[0-9a-fA-F]{4}|\\\\[bfnrt'\"/\\\\]"
|
||||
|
||||
# double-quoted verbatim string
|
||||
- constant.string:
|
||||
start: "@\""
|
||||
end: "\""
|
||||
skip: "\\\\\""
|
||||
rules:
|
||||
- constant.specialChar: "\\\\\""
|
||||
|
||||
# single-quoted verbatim string
|
||||
- constant.string:
|
||||
start: "@'"
|
||||
end: "'"
|
||||
skip: "\\\\'"
|
||||
rules:
|
||||
- constant.specialChar: "\\\\'"
|
||||
|
||||
# block string
|
||||
- constant.string:
|
||||
# FIXME:
|
||||
# This isn't quite right.
|
||||
# The spec says this:
|
||||
|
||||
# beginning with |||, followed by optional whitespace and a new-line.
|
||||
# The next non-blank line must be prefixed with some non-zero length
|
||||
# whitespace W. The block ends at the first subsequent line that does
|
||||
# not begin with W, and it is an error if this line does not contain
|
||||
# some optional whitespace followed by |||.
|
||||
|
||||
# We need to match ^(\s+) on the first non-blank line after |||
|
||||
# Then we need to skip ^\1.*$
|
||||
|
||||
start: "\\|\\|\\| *$"
|
||||
end: "^ *\\|\\|\\|"
|
||||
rules: []
|
||||
|
||||
# multi-line comment
|
||||
- comment:
|
||||
start: "/\\*"
|
||||
end: "\\*/"
|
||||
rules:
|
||||
- todo: "(TODO|XXX|FIXME):?"
|
||||
|
||||
# single-line comment
|
||||
- comment:
|
||||
start: "#|(//)"
|
||||
end: "$"
|
||||
rules:
|
||||
- todo: "(TODO|XXX|FIXME):?"
|
||||
@@ -13,11 +13,12 @@ rules:
|
||||
# definitions
|
||||
- identifier: "[A-Za-z_][A-Za-z0-9_]*[[:space:]]*[(]"
|
||||
# keywords
|
||||
- statement: "\\b(begin|break|catch|continue|function|elseif|struct|else|end|finally|for|global|local|let|const|if|import|export|using|macro|println|return|try|while|module)\\b"
|
||||
- statement: "\\b(baremodule|begin|break|catch|const|continue|do|else|elseif|end|export|finally|for|function|global|if|import|let|local|macro|module|quote|return|struct|try|using|while)\\b"
|
||||
- statement: "\\b(abstract type|primitive type|mutable struct\\b)"
|
||||
# decorators
|
||||
- identifier.macro: "@[A-Za-z0-9_]+"
|
||||
# operators
|
||||
- symbol.operator: "[-+*/|=%<>&~^]|\\b(isa|in)\\b"
|
||||
- symbol.operator: "[-+*/|=%<>&~^]|\\b(in|isa|where)\\b"
|
||||
# parentheses
|
||||
- symbol.brackets: "([(){}]|\\[|\\])"
|
||||
# numbers
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
filetype: php
|
||||
|
||||
detect:
|
||||
detect:
|
||||
filename: "\\.php[2345s~]?$"
|
||||
|
||||
rules:
|
||||
@@ -18,16 +18,15 @@ rules:
|
||||
- comment: "<!--.+?-->"
|
||||
- default: "<\\?(php|=)\" end=\"\\?>"
|
||||
- identifier.class: "([a-zA-Z0-9_-]+)\\("
|
||||
- preproc: "\\b(require|include)(_once)?\\b"
|
||||
- type: "\\b(var|class|extends|function|echo|case|default|exit|switch|extends|as|define|do|declare|in|trait|interface|[E|e]xception|array|int|string|bool|iterable|void)\\b"
|
||||
- type: "\\b(array|bool(ean)?|callable|callback|float|int(eger)?|iterable|object|resource|mixed|string|void)\\b"
|
||||
- identifier.class: "[a-zA-Z\\\\]+::"
|
||||
- identifier: "\\b([A-Z][a-zA-Z0-9_]+)\\b"
|
||||
- identifier: "([A-Z0-9_]+)[;|\\s|\\)|,]"
|
||||
- type.keyword: "(global|public|private|protected|static|const)"
|
||||
- statement: "(implements|abstract|instanceof|if|else(if)?|endif|namespace|use|as|new|throw|catch|try|while|print|(end)?(foreach)?)\\b"
|
||||
- identifier: "new\\s([a-zA-Z0-9\\\\]+)"
|
||||
- special: "(break|continue|goto|return)"
|
||||
- constant.bool: "(true|false|null|TRUE|FALSE|NULL)"
|
||||
- type.keyword: "\\b(global|final|public|private|protected|static|const|var)\\b"
|
||||
- statement: "\\b(abstract|catch|class|declare|do|else(if)?|end(declare|for(each)?|if|switch|while)|finally|for(each)|function|if|interface|namespace|switch|trait|try|while)\\b"
|
||||
- identifier: "\\bnew\\s+([a-zA-Z0-9\\\\]+)"
|
||||
- special: "\\b(as|and|break|case|clone|continue|default|die|fn|echo|empty|eval|exit|extends|goto|or|include(_once)?|implements|instanceof|insteadof|isset|list|new|print|return|require(_once)?|unset|use|throw|xor|yield(\\s+from))\\b"
|
||||
- constant.bool: "\\b(true|false|null|TRUE|FALSE|NULL)\\b"
|
||||
- constant: "[\\s|=|\\s|\\(|/|+|-|\\*|\\[]"
|
||||
- constant.number: "[0-9]"
|
||||
- identifier: "(\\$this|parent|self|\\$this->)"
|
||||
|
||||
@@ -20,8 +20,8 @@ rules:
|
||||
- statement: "--[a-z-]+"
|
||||
- statement: "\\ -[a-z]+"
|
||||
|
||||
- identifier: "\\$\\{?[0-9A-Z_!@#$*?-]+\\}?"
|
||||
- identifier: "\\$\\{?[0-9A-Z_!@#$*?-]+\\}?"
|
||||
- identifier: "\\$\\{?[0-9A-Za-z_!@#$*?-]+\\}?"
|
||||
- identifier: "\\$\\{?[0-9A-Za-z_!@#$*?-]+\\}?"
|
||||
|
||||
- constant.string:
|
||||
start: "\""
|
||||
|
||||
72
runtime/syntax/zscript.yaml
Normal file
72
runtime/syntax/zscript.yaml
Normal file
@@ -0,0 +1,72 @@
|
||||
filetype: zscript
|
||||
# Loosely based on the csharp.yaml definition
|
||||
# (?i) on everything because ZScript isn't case sensitive
|
||||
|
||||
detect:
|
||||
filename: "(?i)\\.z(c|sc)$"
|
||||
|
||||
rules:
|
||||
|
||||
# ZScript only has one preprocessor directive and a required engine version declaration
|
||||
- preproc: "(?i)#include"
|
||||
- preproc: "(?i)version"
|
||||
|
||||
# State labels ("goto" word overridden by state logic rule below)
|
||||
- symbol.tag: "(?i)[a-z0-9.]+:"
|
||||
- symbol.tag: "(?i)goto [a-z0-9]+[\\+0-9]*"
|
||||
|
||||
# Classes
|
||||
- identifier.class: "(?i)class +[a-z0-9_]+ *((:) +[a-z0-9.]+)?"
|
||||
|
||||
# Functions (open paren overridden by symbol.brackets rule because perl regex apparently doesn't support postive lookahead)
|
||||
- identifier: "(?i)[\\.]*[a-z0-9_]+[ ]*[(]+"
|
||||
|
||||
# Variable types
|
||||
- type: "(?i)\\b(actor|object|vector2|vector3|name|string|color|sound|void|double|bool|int|float|float64|uint8|uint16|uint|int8|int16|TextureID|SpriteID|Array|voidptr|short|action|state|statelabel)\\b"
|
||||
|
||||
# Keywords
|
||||
- statement: "(?i)\\b(class|default|private|static|native|return|if|else|for|while|do|deprecated|null|readonly|true|false|struct|extend|clearscope|vararg|ui|play|virtual|virtualscope|meta|Property|in|out|states|override|super|is|let|const|replaces|protected|self|abstract|enum|switch|case)\\b"
|
||||
|
||||
# State logic keywords
|
||||
- special: "(?i)\\b(goto|loop|stop|break|continue|fail)\\b"
|
||||
|
||||
# Symbols
|
||||
- symbol.operator: "[\\-+/*=<>?:!~%&|]"
|
||||
- symbol.brackets: "[(){}]|\\[|\\]"
|
||||
|
||||
# Constants
|
||||
- constant.bool: "(?i)(\\b(true|false)\\b|NULL)"
|
||||
- constant.number: "(?i)\\b([0-9][.]*[0-9]*)+?\\b"
|
||||
- constant.number: "(?i)\\b(0x[A-Fa-f0-9_]+)?\\b"
|
||||
- constant.number: "(?i)\\b(0b[0-1_]+)[FL]?\\b"
|
||||
#- constant.number: "(?i)\\b(([0-9][.]*[0-9]*)+|0x[A-Fa-f0-9_]+|0b[0-1_]+)[FL]?\\b"
|
||||
|
||||
# Strings
|
||||
- constant.string:
|
||||
start: "\""
|
||||
end: "\""
|
||||
skip: "\\\\."
|
||||
rules:
|
||||
- constant.specialChar: "\\\\([btnfr]|'|\\\"|\\\\)"
|
||||
- constant.specialChar: "\\\\u[A-Fa-f0-9]{4}"
|
||||
|
||||
- constant.string:
|
||||
start: "'"
|
||||
end: "'"
|
||||
skip: "\\\\."
|
||||
rules:
|
||||
- constant.specialChar: "\\\\([btnfr]|'|\\\"|\\\\)"
|
||||
- constant.specialChar: "\\\\u[A-Fa-f0-9]{4}"
|
||||
|
||||
# Comments
|
||||
- comment:
|
||||
start: "//"
|
||||
end: "$"
|
||||
rules:
|
||||
- todo: "(TODO|XXX|FIXME):?"
|
||||
|
||||
- comment:
|
||||
start: "/\\*"
|
||||
end: "\\*/"
|
||||
rules:
|
||||
- todo: "(TODO|XXX|FIXME):?"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user