Refactor and clean up
This commit puts in place the ability for multiple views (splits). This commit also removes the editor bindings so that all bindings can be rebound by the user. I also added some more comments This fixes #109
This commit is contained in:
parent
d9d0af4a99
commit
e8d8da1443
9 changed files with 311 additions and 248 deletions
|
@ -71,9 +71,13 @@ func InitBindings() {
|
|||
"HalfPageDown": (*View).HalfPageDown,
|
||||
"StartOfLine": (*View).StartOfLine,
|
||||
"EndOfLine": (*View).EndOfLine,
|
||||
"ToggleHelp": (*View).ToggleHelp,
|
||||
"ToggleRuler": (*View).ToggleRuler,
|
||||
"JumpLine": (*View).JumpLine,
|
||||
"ClearStatus": (*View).ClearStatus,
|
||||
"ShellMode": (*View).ShellMode,
|
||||
"CommandMode": (*View).CommandMode,
|
||||
"Quit": (*View).Quit,
|
||||
}
|
||||
|
||||
keys := map[string]Key{
|
||||
|
@ -303,13 +307,14 @@ func DefaultBindings() map[string]string {
|
|||
"End": "End",
|
||||
"PgUp": "PageUp",
|
||||
"PgDn": "PageDown",
|
||||
// Find alternative key
|
||||
// "CtrlU": "HalfPageUp",
|
||||
// "CtrlD": "HalfPageDown",
|
||||
"CtrlR": "ToggleRuler",
|
||||
"CtrlL": "JumpLine",
|
||||
"Delete": "Delete",
|
||||
"Esc": "ClearStatus",
|
||||
"CtrlG": "ToggleHelp",
|
||||
"CtrlR": "ToggleRuler",
|
||||
"CtrlL": "JumpLine",
|
||||
"Delete": "Delete",
|
||||
"Esc": "ClearStatus",
|
||||
"CtrlB": "ShellMode",
|
||||
"CtrlQ": "Quit",
|
||||
"CtrlE": "CommandMode",
|
||||
|
||||
// Emacs-style keybindings
|
||||
"Alt-f": "WordRight",
|
||||
|
@ -625,6 +630,10 @@ func (v *View) InsertTab() bool {
|
|||
|
||||
// Save the buffer to disk
|
||||
func (v *View) Save() bool {
|
||||
if v.helpOpen {
|
||||
// We can't save the help text
|
||||
return false
|
||||
}
|
||||
// If this is an empty buffer, ask for a filename
|
||||
if v.Buf.Path == "" {
|
||||
filename, canceled := messenger.Prompt("Filename: ")
|
||||
|
@ -894,6 +903,53 @@ func (v *View) ClearStatus() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// ToggleHelp toggles the help screen
|
||||
func (v *View) ToggleHelp() bool {
|
||||
if !v.helpOpen {
|
||||
v.lastBuffer = v.Buf
|
||||
helpBuffer := NewBuffer(helpTxt, "help.md")
|
||||
helpBuffer.Name = "Help"
|
||||
v.helpOpen = true
|
||||
v.OpenBuffer(helpBuffer)
|
||||
} else {
|
||||
v.OpenBuffer(v.lastBuffer)
|
||||
v.helpOpen = false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ShellMode opens a terminal to run a shell command
|
||||
func (v *View) ShellMode() bool {
|
||||
input, canceled := messenger.Prompt("$ ")
|
||||
if !canceled {
|
||||
// The true here is for openTerm to make the command interactive
|
||||
HandleShellCommand(input, true)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CommandMode lets the user enter a command
|
||||
func (v *View) CommandMode() bool {
|
||||
input, canceled := messenger.Prompt("> ")
|
||||
if !canceled {
|
||||
HandleCommand(input)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Quit quits the editor
|
||||
// This behavior needs to be changed and should really only quit the editor if this
|
||||
// is the last view
|
||||
// However, since micro only supports one view for now, it doesn't really matter
|
||||
func (v *View) Quit() bool {
|
||||
// Make sure not to quit if there are unsaved changes
|
||||
if views[mainView].CanClose("Quit anyway? (yes, no, save) ") {
|
||||
screen.Fini()
|
||||
os.Exit(0)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// None is no action
|
||||
func None() bool {
|
||||
return false
|
||||
|
|
|
@ -24,10 +24,13 @@ func RunShellCommand(input string) (string, error) {
|
|||
return outstring, err
|
||||
}
|
||||
|
||||
// HandleShellCommand runs the shell command and outputs to DisplayBlock
|
||||
func HandleShellCommand(input string, view *View, openTerm bool) {
|
||||
// HandleShellCommand runs the shell command
|
||||
// The openTerm argument specifies whether a terminal should be opened (for viewing output
|
||||
// or interacting with stdin)
|
||||
func HandleShellCommand(input string, openTerm bool) {
|
||||
inputCmd := strings.Split(input, " ")[0]
|
||||
if !openTerm {
|
||||
// Simply run the command in the background and notify the user when it's done
|
||||
messenger.Message("Running...")
|
||||
go func() {
|
||||
output, err := RunShellCommand(input)
|
||||
|
@ -42,19 +45,24 @@ func HandleShellCommand(input string, view *View, openTerm bool) {
|
|||
} else {
|
||||
messenger.Message(output)
|
||||
}
|
||||
Redraw(view)
|
||||
// We have to make sure to redraw
|
||||
RedrawAll()
|
||||
}()
|
||||
} else {
|
||||
// Shut down the screen because we're going to interact directly with the shell
|
||||
screen.Fini()
|
||||
screen = nil
|
||||
|
||||
args := strings.Split(input, " ")[1:]
|
||||
|
||||
// Set up everything for the command
|
||||
cmd := exec.Command(inputCmd, args...)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
// This is a trap for Ctrl-C so that it doesn't kill micro
|
||||
// Instead we trap Ctrl-C to kill the program we're running
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
|
@ -63,51 +71,52 @@ func HandleShellCommand(input string, view *View, openTerm bool) {
|
|||
}
|
||||
}()
|
||||
|
||||
// Start the command
|
||||
cmd.Start()
|
||||
cmd.Wait()
|
||||
|
||||
// This is just so we don't return right away and let the user press enter to return
|
||||
TermMessage("")
|
||||
|
||||
// Start the screen back up
|
||||
InitScreen()
|
||||
}
|
||||
}
|
||||
|
||||
// HandleCommand handles input from the user
|
||||
func HandleCommand(input string, view *View) {
|
||||
func HandleCommand(input string) {
|
||||
inputCmd := strings.Split(input, " ")[0]
|
||||
args := strings.Split(input, " ")[1:]
|
||||
|
||||
commands := []string{"set", "quit", "save", "replace", "run"}
|
||||
|
||||
i := 0
|
||||
cmd := inputCmd
|
||||
|
||||
for _, c := range commands {
|
||||
if strings.HasPrefix(c, inputCmd) {
|
||||
i++
|
||||
cmd = c
|
||||
}
|
||||
}
|
||||
if i == 1 {
|
||||
inputCmd = cmd
|
||||
}
|
||||
|
||||
switch inputCmd {
|
||||
case "set":
|
||||
SetOption(view, args)
|
||||
// Set an option and we have to set it for every view
|
||||
for _, view := range views {
|
||||
SetOption(view, args)
|
||||
}
|
||||
case "run":
|
||||
HandleShellCommand(strings.Join(args, " "), view, false)
|
||||
// Run a shell command in the background (openTerm is false)
|
||||
HandleShellCommand(strings.Join(args, " "), false)
|
||||
case "quit":
|
||||
if view.CanClose("Quit anyway? (yes, no, save) ") {
|
||||
// This is a bit weird because micro only has one view for now so there is no way to close
|
||||
// a single view
|
||||
// Currently if multiple views were open, it would close all of them, and not check the non-mainviews
|
||||
// for unsaved changes. This, and the behavior of Ctrl-Q need to be changed when splits are implemented
|
||||
if views[mainView].CanClose("Quit anyway? (yes, no, save) ") {
|
||||
screen.Fini()
|
||||
os.Exit(0)
|
||||
}
|
||||
case "save":
|
||||
view.Save()
|
||||
// Save the main view
|
||||
views[mainView].Save()
|
||||
case "replace":
|
||||
// This is a regex to parse the replace expression
|
||||
// We allow no quotes if there are no spaces, but if you want to search
|
||||
// for or replace an expression with spaces, you can add double quotes
|
||||
r := regexp.MustCompile(`"[^"\\]*(?:\\.[^"\\]*)*"|[^\s]*`)
|
||||
replaceCmd := r.FindAllString(strings.Join(args, " "), -1)
|
||||
if len(replaceCmd) < 2 {
|
||||
// We need to find both a search and replace expression
|
||||
messenger.Error("Invalid replace statement: " + strings.Join(args, " "))
|
||||
return
|
||||
}
|
||||
|
@ -121,6 +130,7 @@ func HandleCommand(input string, view *View) {
|
|||
search := string(replaceCmd[0])
|
||||
replace := string(replaceCmd[1])
|
||||
|
||||
// If the search and replace expressions have quotes, we need to remove those
|
||||
if strings.HasPrefix(search, `"`) && strings.HasSuffix(search, `"`) {
|
||||
search = search[1 : len(search)-1]
|
||||
}
|
||||
|
@ -128,17 +138,19 @@ func HandleCommand(input string, view *View) {
|
|||
replace = replace[1 : len(replace)-1]
|
||||
}
|
||||
|
||||
// We replace all escaped double quotes to real double quotes
|
||||
search = strings.Replace(search, `\"`, `"`, -1)
|
||||
replace = strings.Replace(replace, `\"`, `"`, -1)
|
||||
|
||||
// messenger.Error(search + " -> " + replace)
|
||||
|
||||
regex, err := regexp.Compile(search)
|
||||
if err != nil {
|
||||
// There was an error with the user's regex
|
||||
messenger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
view := views[mainView]
|
||||
|
||||
found := false
|
||||
for {
|
||||
match := regex.FindStringIndex(view.Buf.String())
|
||||
|
@ -150,7 +162,7 @@ func HandleCommand(input string, view *View) {
|
|||
// The 'check' flag was used
|
||||
Search(search, view, true)
|
||||
view.Relocate()
|
||||
Redraw(view)
|
||||
RedrawAll()
|
||||
choice, canceled := messenger.YesNoPrompt("Perform replacement? (y,n)")
|
||||
if canceled {
|
||||
if view.Cursor.HasSelection() {
|
||||
|
|
|
@ -30,7 +30,8 @@ var (
|
|||
// Object to send messages and prompts to the user
|
||||
messenger *Messenger
|
||||
|
||||
// The default style
|
||||
// The default highlighting style
|
||||
// This simply defines the default foreground and background colors
|
||||
defStyle tcell.Style
|
||||
|
||||
// Where the user's configuration is
|
||||
|
@ -38,20 +39,25 @@ var (
|
|||
// If $XDG_CONFIG_HOME is not set, it is ~/.config/micro
|
||||
configDir string
|
||||
|
||||
// Version is the version number.
|
||||
// Version is the version number or commit hash
|
||||
// This should be set by the linker
|
||||
Version = "Unknown"
|
||||
|
||||
// Is the help screen open
|
||||
helpOpen = false
|
||||
|
||||
// L is the lua state
|
||||
// This is the VM that runs the plugins
|
||||
L *lua.LState
|
||||
|
||||
// The list of views
|
||||
views []*View
|
||||
// This is the currently open view
|
||||
// It's just an index to the view in the views array
|
||||
mainView int
|
||||
)
|
||||
|
||||
// LoadInput loads the file input for the editor
|
||||
func LoadInput() (string, []byte, error) {
|
||||
// There are a number of ways micro should start given its input
|
||||
|
||||
// 1. If it is given a file in os.Args, it should open that
|
||||
|
||||
// 2. If there is no input file and the input is not a terminal, that means
|
||||
|
@ -85,15 +91,15 @@ func LoadInput() (string, []byte, error) {
|
|||
return filename, input, err
|
||||
}
|
||||
|
||||
// InitConfigDir finds the configuration directory for micro according to the
|
||||
// XDG spec.
|
||||
// InitConfigDir finds the configuration directory for micro according to the XDG spec.
|
||||
// If no directory is found, it creates one.
|
||||
func InitConfigDir() {
|
||||
xdgHome := os.Getenv("XDG_CONFIG_HOME")
|
||||
if xdgHome == "" {
|
||||
// The user has not set $XDG_CONFIG_HOME so we should act like it was set to ~/.config
|
||||
home, err := homedir.Dir()
|
||||
if err != nil {
|
||||
TermMessage("Error finding your home directory\nCan't load syntax files")
|
||||
TermMessage("Error finding your home directory\nCan't load config files")
|
||||
return
|
||||
}
|
||||
xdgHome = home + "/.config"
|
||||
|
@ -101,6 +107,7 @@ func InitConfigDir() {
|
|||
configDir = xdgHome + "/micro"
|
||||
|
||||
if _, err := os.Stat(xdgHome); os.IsNotExist(err) {
|
||||
// If the xdgHome doesn't exist we should create it
|
||||
err = os.Mkdir(xdgHome, os.ModePerm)
|
||||
if err != nil {
|
||||
TermMessage("Error creating XDG_CONFIG_HOME directory: " + err.Error())
|
||||
|
@ -108,6 +115,7 @@ func InitConfigDir() {
|
|||
}
|
||||
|
||||
if _, err := os.Stat(configDir); os.IsNotExist(err) {
|
||||
// If the micro specific config directory doesn't exist we should create that too
|
||||
err = os.Mkdir(configDir, os.ModePerm)
|
||||
if err != nil {
|
||||
TermMessage("Error creating configuration directory: " + err.Error())
|
||||
|
@ -150,6 +158,7 @@ func InitScreen() {
|
|||
Background(tcell.ColorDefault)
|
||||
|
||||
// There may be another default style defined in the colorscheme
|
||||
// In that case we should use that one
|
||||
if style, ok := colorscheme["default"]; ok {
|
||||
defStyle = style
|
||||
}
|
||||
|
@ -158,10 +167,12 @@ func InitScreen() {
|
|||
screen.EnableMouse()
|
||||
}
|
||||
|
||||
// Redraw redraws the screen and the given view
|
||||
func Redraw(view *View) {
|
||||
// RedrawAll redraws everything -- all the views and the messenger
|
||||
func RedrawAll() {
|
||||
screen.Clear()
|
||||
view.Display()
|
||||
for _, v := range views {
|
||||
v.Display()
|
||||
}
|
||||
messenger.Display()
|
||||
screen.Show()
|
||||
}
|
||||
|
@ -184,6 +195,7 @@ func main() {
|
|||
L = lua.NewState()
|
||||
defer L.Close()
|
||||
|
||||
// Some encoding stuff in case the user isn't using UTF-8
|
||||
encoding.Register()
|
||||
tcell.SetEncodingFallback(tcell.EncodingFallbackASCII)
|
||||
|
||||
|
@ -203,6 +215,7 @@ func main() {
|
|||
|
||||
// This is just so if we have an error, we can exit cleanly and not completely
|
||||
// mess up the terminal being worked in
|
||||
// In other words we need to shut down tcell before the program crashes
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
screen.Fini()
|
||||
|
@ -214,10 +227,12 @@ func main() {
|
|||
}()
|
||||
|
||||
messenger = new(Messenger)
|
||||
view := NewView(buf)
|
||||
views = make([]*View, 1)
|
||||
views[0] = NewView(buf)
|
||||
|
||||
L.SetGlobal("OS", luar.New(L, runtime.GOOS))
|
||||
L.SetGlobal("view", luar.New(L, view))
|
||||
L.SetGlobal("views", luar.New(L, views))
|
||||
L.SetGlobal("mainView", luar.New(L, mainView))
|
||||
L.SetGlobal("messenger", luar.New(L, messenger))
|
||||
L.SetGlobal("GetOption", luar.New(L, GetOption))
|
||||
L.SetGlobal("AddOption", luar.New(L, AddOption))
|
||||
|
@ -226,54 +241,18 @@ func main() {
|
|||
|
||||
for {
|
||||
// Display everything
|
||||
Redraw(view)
|
||||
RedrawAll()
|
||||
|
||||
// Wait for the user's action
|
||||
event := screen.PollEvent()
|
||||
|
||||
if searching {
|
||||
HandleSearchEvent(event, view)
|
||||
// Since searching is done in real time, we need to redraw every time
|
||||
// there is a new event in the search bar
|
||||
HandleSearchEvent(event, views[mainView])
|
||||
} else {
|
||||
// Check if we should quit
|
||||
switch e := event.(type) {
|
||||
case *tcell.EventKey:
|
||||
switch e.Key() {
|
||||
case tcell.KeyCtrlQ:
|
||||
// Make sure not to quit if there are unsaved changes
|
||||
if helpOpen {
|
||||
view.OpenBuffer(buf)
|
||||
helpOpen = false
|
||||
} else {
|
||||
if view.CanClose("Quit anyway? (yes, no, save) ") {
|
||||
screen.Fini()
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
case tcell.KeyCtrlE:
|
||||
input, canceled := messenger.Prompt("> ")
|
||||
if !canceled {
|
||||
HandleCommand(input, view)
|
||||
}
|
||||
case tcell.KeyCtrlB:
|
||||
input, canceled := messenger.Prompt("$ ")
|
||||
if !canceled {
|
||||
HandleShellCommand(input, view, true)
|
||||
}
|
||||
case tcell.KeyCtrlG:
|
||||
if !helpOpen {
|
||||
helpBuffer := NewBuffer(helpTxt, "help.md")
|
||||
helpBuffer.Name = "Help"
|
||||
helpOpen = true
|
||||
view.OpenBuffer(helpBuffer)
|
||||
} else {
|
||||
view.OpenBuffer(buf)
|
||||
helpOpen = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send it to the view
|
||||
view.HandleEvent(event)
|
||||
views[mainView].HandleEvent(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -41,7 +41,7 @@ func (sline *Statusline) Display() {
|
|||
file += " " + sline.view.Buf.FileType
|
||||
|
||||
rightText := "Ctrl-g for help "
|
||||
if helpOpen {
|
||||
if sline.view.helpOpen {
|
||||
rightText = "Ctrl-g to close help "
|
||||
}
|
||||
|
||||
|
|
|
@ -37,8 +37,17 @@ type View struct {
|
|||
// Holds the list of gutter messages
|
||||
messages map[string][]GutterMessage
|
||||
|
||||
// Is the help text opened in this view
|
||||
helpOpen bool
|
||||
|
||||
// Is this view modifiable?
|
||||
Modifiable bool
|
||||
|
||||
// The buffer
|
||||
Buf *Buffer
|
||||
// This is the buffer that was last opened
|
||||
// This is used to open help, and then go back to the previously opened buffer
|
||||
lastBuffer *Buffer
|
||||
// The statusline
|
||||
sline Statusline
|
||||
|
||||
|
|
|
@ -21,15 +21,10 @@ You can move the cursor around with the arrow keys and mouse.
|
|||
These are the default keybindings, along with their actions.
|
||||
|
||||
|
||||
#### Editor bindings
|
||||
|
||||
* Ctrl-q: Quit
|
||||
* Ctrl-e: Execute a command
|
||||
* Ctrl-g: Toggle help text
|
||||
* Ctrl-b: Run a shell command
|
||||
|
||||
#### Buffer bindings
|
||||
|
||||
* Ctrl-s: Save
|
||||
* Ctrl-o: Open file
|
||||
* Ctrl-z: Undo
|
||||
|
@ -55,7 +50,7 @@ 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.
|
||||
|
||||
The buffer bindings may be rebound using the `~/.config/micro/bindings.json`
|
||||
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
|
||||
|
@ -72,52 +67,64 @@ Here are the defaults:
|
|||
|
||||
```json
|
||||
{
|
||||
"Up": "CursorUp",
|
||||
"Down": "CursorDown",
|
||||
"Right": "CursorRight",
|
||||
"Left": "CursorLeft",
|
||||
"ShiftUp": "SelectUp",
|
||||
"ShiftDown": "SelectDown",
|
||||
"ShiftLeft": "SelectLeft",
|
||||
"ShiftRight": "SelectRight",
|
||||
"AltLeft": "WordLeft",
|
||||
"AltRight": "WordRight",
|
||||
"AltShiftRight": "SelectWordRight",
|
||||
"AltShiftLeft": "SelectWordLeft",
|
||||
"CtrlLeft": "StartOfLine",
|
||||
"CtrlRight": "EndOfLine",
|
||||
"CtrlShiftLeft": "SelectToStartOfLine",
|
||||
"CtrlShiftRight": "SelectToEndOfLine",
|
||||
"CtrlUp": "CursorStart",
|
||||
"CtrlDown": "CursorEnd",
|
||||
"CtrlShiftUp": "SelectToStart",
|
||||
"CtrlShiftDown": "SelectToEnd",
|
||||
"Enter": "InsertEnter",
|
||||
"Space": "InsertSpace",
|
||||
"Backspace": "Backspace",
|
||||
"Backspace2": "Backspace",
|
||||
"Tab": "InsertTab",
|
||||
"CtrlO": "OpenFile",
|
||||
"CtrlS": "Save",
|
||||
"CtrlF": "Find",
|
||||
"CtrlN": "FindNext",
|
||||
"CtrlP": "FindPrevious",
|
||||
"CtrlZ": "Undo",
|
||||
"CtrlY": "Redo",
|
||||
"CtrlC": "Copy",
|
||||
"CtrlX": "Cut",
|
||||
"CtrlK": "CutLine",
|
||||
"CtrlD": "DuplicateLine",
|
||||
"CtrlV": "Paste",
|
||||
"CtrlA": "SelectAll",
|
||||
"Home": "Start",
|
||||
"End": "End",
|
||||
"PgUp": "PageUp",
|
||||
"PgDn": "PageDown",
|
||||
"CtrlU": "HalfPageUp",
|
||||
"CtrlD": "HalfPageDown",
|
||||
"CtrlR": "ToggleRuler",
|
||||
"Delete": "Delete"
|
||||
"Up": "CursorUp",
|
||||
"Down": "CursorDown",
|
||||
"Right": "CursorRight",
|
||||
"Left": "CursorLeft",
|
||||
"ShiftUp": "SelectUp",
|
||||
"ShiftDown": "SelectDown",
|
||||
"ShiftLeft": "SelectLeft",
|
||||
"ShiftRight": "SelectRight",
|
||||
"AltLeft": "WordLeft",
|
||||
"AltRight": "WordRight",
|
||||
"AltShiftRight": "SelectWordRight",
|
||||
"AltShiftLeft": "SelectWordLeft",
|
||||
"CtrlLeft": "StartOfLine",
|
||||
"CtrlRight": "EndOfLine",
|
||||
"CtrlShiftLeft": "SelectToStartOfLine",
|
||||
"CtrlShiftRight": "SelectToEndOfLine",
|
||||
"CtrlUp": "CursorStart",
|
||||
"CtrlDown": "CursorEnd",
|
||||
"CtrlShiftUp": "SelectToStart",
|
||||
"CtrlShiftDown": "SelectToEnd",
|
||||
"Enter": "InsertEnter",
|
||||
"Space": "InsertSpace",
|
||||
"Backspace": "Backspace",
|
||||
"Backspace2": "Backspace",
|
||||
"Tab": "InsertTab",
|
||||
"CtrlO": "OpenFile",
|
||||
"CtrlS": "Save",
|
||||
"CtrlF": "Find",
|
||||
"CtrlN": "FindNext",
|
||||
"CtrlP": "FindPrevious",
|
||||
"CtrlZ": "Undo",
|
||||
"CtrlY": "Redo",
|
||||
"CtrlC": "Copy",
|
||||
"CtrlX": "Cut",
|
||||
"CtrlK": "CutLine",
|
||||
"CtrlD": "DuplicateLine",
|
||||
"CtrlV": "Paste",
|
||||
"CtrlA": "SelectAll",
|
||||
"Home": "Start",
|
||||
"End": "End",
|
||||
"PgUp": "PageUp",
|
||||
"PgDn": "PageDown",
|
||||
"CtrlG": "ToggleHelp",
|
||||
"CtrlR": "ToggleRuler",
|
||||
"CtrlL": "JumpLine",
|
||||
"Delete": "Delete",
|
||||
"Esc": "ClearStatus",
|
||||
"CtrlB": "ShellMode",
|
||||
"CtrlQ": "Quit",
|
||||
"CtrlE": "CommandMode",
|
||||
|
||||
// Emacs-style keybindings
|
||||
"Alt-f": "WordRight",
|
||||
"Alt-b": "WordLeft",
|
||||
"Alt-a": "StartOfLine",
|
||||
"Alt-e": "EndOfLine",
|
||||
"Alt-p": "CursorUp",
|
||||
"Alt-n": "CursorDown",
|
||||
}
|
||||
|
||||
```
|
||||
|
@ -211,7 +218,7 @@ Here are the options that you can set:
|
|||
|
||||
default value: `3`
|
||||
|
||||
* `scrollspeed`: amount of lines to scroll
|
||||
* `scrollspeed`: amount of lines to scroll for one scroll event
|
||||
|
||||
default value: `2`
|
||||
|
||||
|
|
|
@ -6,25 +6,25 @@ if GetOption("gofmt") == nil then
|
|||
end
|
||||
|
||||
function go_onSave()
|
||||
if view.Buf.FileType == "Go" then
|
||||
if views[mainView+1].Buf.FileType == "Go" then
|
||||
if GetOption("goimports") then
|
||||
go_goimports()
|
||||
elseif GetOption("gofmt") then
|
||||
go_gofmt()
|
||||
end
|
||||
|
||||
view:ReOpen()
|
||||
views[mainView+1]:ReOpen()
|
||||
end
|
||||
end
|
||||
|
||||
function go_gofmt()
|
||||
local handle = io.popen("gofmt -w " .. view.Buf.Path)
|
||||
local handle = io.popen("gofmt -w " .. views[mainView+1].Buf.Path)
|
||||
local result = handle:read("*a")
|
||||
handle:close()
|
||||
end
|
||||
|
||||
function go_goimports()
|
||||
local handle = io.popen("goimports -w " .. view.Buf.Path)
|
||||
local handle = io.popen("goimports -w " .. views[mainView+1].Buf.Path)
|
||||
local result = go_split(handle:read("*a"), ":")
|
||||
handle:close()
|
||||
end
|
||||
|
|
|
@ -4,15 +4,15 @@ end
|
|||
|
||||
function linter_onSave()
|
||||
if GetOption("linter") then
|
||||
local ft = view.Buf.FileType
|
||||
local file = view.Buf.Path
|
||||
local ft = views[mainView+1].Buf.FileType
|
||||
local file = views[mainView+1].Buf.Path
|
||||
local devnull = "/dev/null"
|
||||
if OS == "windows" then
|
||||
devnull = "NUL"
|
||||
end
|
||||
if ft == "Go" then
|
||||
linter_lint("gobuild", "go build -o " .. devnull, "%f:%l: %m")
|
||||
linter_lint("golint", "golint " .. view.Buf.Path, "%f:%l:%d+: %m")
|
||||
linter_lint("golint", "golint " .. views[mainView+1].Buf.Path, "%f:%l:%d+: %m")
|
||||
elseif ft == "Lua" then
|
||||
linter_lint("luacheck", "luacheck --no-color " .. file, "%f:%l:%d+: %m")
|
||||
elseif ft == "Python" then
|
||||
|
@ -27,12 +27,12 @@ function linter_onSave()
|
|||
linter_lint("jshint", "jshint " .. file, "%f: line %l,.+, %m")
|
||||
end
|
||||
else
|
||||
view:ClearAllGutterMessages()
|
||||
views[mainView+1]:ClearAllGutterMessages()
|
||||
end
|
||||
end
|
||||
|
||||
function linter_lint(linter, cmd, errorformat)
|
||||
view:ClearGutterMessages(linter)
|
||||
views[mainView+1]:ClearGutterMessages(linter)
|
||||
|
||||
local handle = io.popen("(" .. cmd .. ")" .. " 2>&1")
|
||||
local lines = linter_split(handle:read("*a"), "\n")
|
||||
|
@ -44,8 +44,8 @@ function linter_lint(linter, cmd, errorformat)
|
|||
line = line:match("^%s*(.+)%s*$")
|
||||
if string.find(line, regex) then
|
||||
local file, line, msg = string.match(line, regex)
|
||||
if linter_basename(view.Buf.Path) == linter_basename(file) then
|
||||
view:GutterMessage(linter, tonumber(line), msg, 2)
|
||||
if linter_basename(views[mainView+1].Buf.Path) == linter_basename(file) then
|
||||
views[mainView+1]:GutterMessage(linter, tonumber(line), msg, 2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue