From 970bb7850077cf9e4033c9618876a25e74cc2e6c Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Mon, 27 Aug 2018 19:53:08 -0400 Subject: [PATCH] Action subpackage --- cmd/micro/{ => action}/actions.go | 192 ++++++++-------- cmd/micro/{ => action}/actions_other.go | 4 +- cmd/micro/{ => action}/actions_posix.go | 4 +- cmd/micro/{ => action}/bindings.go | 6 +- cmd/micro/action/bufhandler.go | 206 +++++++++++++++++ .../{actionhandler.go => action/handler.go} | 6 +- cmd/micro/bufactionhandler.go | 208 ------------------ cmd/micro/buffer/buffer.go | 202 ----------------- cmd/micro/buffer/save.go | 160 ++++++++++++++ cmd/micro/buffer/serialize.go | 61 +++++ cmd/micro/micro.go | 7 +- cmd/micro/statusline.go | 3 +- 12 files changed, 539 insertions(+), 520 deletions(-) rename cmd/micro/{ => action}/actions.go (61%) rename cmd/micro/{ => action}/actions_other.go (56%) rename cmd/micro/{ => action}/actions_posix.go (92%) rename cmd/micro/{ => action}/bindings.go (99%) create mode 100644 cmd/micro/action/bufhandler.go rename cmd/micro/{actionhandler.go => action/handler.go} (87%) delete mode 100644 cmd/micro/bufactionhandler.go create mode 100644 cmd/micro/buffer/save.go create mode 100644 cmd/micro/buffer/serialize.go diff --git a/cmd/micro/actions.go b/cmd/micro/action/actions.go similarity index 61% rename from cmd/micro/actions.go rename to cmd/micro/action/actions.go index 2f39fe8c..db56279a 100644 --- a/cmd/micro/actions.go +++ b/cmd/micro/action/actions.go @@ -1,4 +1,4 @@ -package main +package action import ( "os" @@ -9,450 +9,450 @@ import ( // MousePress is the event that should happen when a normal click happens // This is almost always bound to left click -func (a *BufActionHandler) MousePress(e *tcell.EventMouse) bool { +func (a *BufHandler) MousePress(e *tcell.EventMouse) bool { return false } // ScrollUpAction scrolls the view up -func (a *BufActionHandler) ScrollUpAction() bool { +func (a *BufHandler) ScrollUpAction() bool { return false } // ScrollDownAction scrolls the view up -func (a *BufActionHandler) ScrollDownAction() bool { +func (a *BufHandler) ScrollDownAction() bool { return false } // Center centers the view on the cursor -func (a *BufActionHandler) Center() bool { +func (a *BufHandler) Center() bool { return true } // CursorUp moves the cursor up -func (a *BufActionHandler) CursorUp() bool { +func (a *BufHandler) CursorUp() bool { a.Cursor.Deselect(true) a.Cursor.Up() return true } // CursorDown moves the cursor down -func (a *BufActionHandler) CursorDown() bool { +func (a *BufHandler) CursorDown() bool { a.Cursor.Deselect(true) a.Cursor.Down() return true } // CursorLeft moves the cursor left -func (a *BufActionHandler) CursorLeft() bool { +func (a *BufHandler) CursorLeft() bool { a.Cursor.Deselect(true) a.Cursor.Left() return true } // CursorRight moves the cursor right -func (a *BufActionHandler) CursorRight() bool { +func (a *BufHandler) CursorRight() bool { a.Cursor.Deselect(true) a.Cursor.Right() return true } // WordRight moves the cursor one word to the right -func (a *BufActionHandler) WordRight() bool { +func (a *BufHandler) WordRight() bool { return true } // WordLeft moves the cursor one word to the left -func (a *BufActionHandler) WordLeft() bool { +func (a *BufHandler) WordLeft() bool { return true } // SelectUp selects up one line -func (a *BufActionHandler) SelectUp() bool { +func (a *BufHandler) SelectUp() bool { return true } // SelectDown selects down one line -func (a *BufActionHandler) SelectDown() bool { +func (a *BufHandler) SelectDown() bool { return true } // SelectLeft selects the character to the left of the cursor -func (a *BufActionHandler) SelectLeft() bool { +func (a *BufHandler) SelectLeft() bool { return true } // SelectRight selects the character to the right of the cursor -func (a *BufActionHandler) SelectRight() bool { +func (a *BufHandler) SelectRight() bool { return true } // SelectWordRight selects the word to the right of the cursor -func (a *BufActionHandler) SelectWordRight() bool { +func (a *BufHandler) SelectWordRight() bool { return true } // SelectWordLeft selects the word to the left of the cursor -func (a *BufActionHandler) SelectWordLeft() bool { +func (a *BufHandler) SelectWordLeft() bool { return true } // StartOfLine moves the cursor to the start of the line -func (a *BufActionHandler) StartOfLine() bool { +func (a *BufHandler) StartOfLine() bool { return true } // EndOfLine moves the cursor to the end of the line -func (a *BufActionHandler) EndOfLine() bool { +func (a *BufHandler) EndOfLine() bool { return true } // SelectLine selects the entire current line -func (a *BufActionHandler) SelectLine() bool { +func (a *BufHandler) SelectLine() bool { return true } // SelectToStartOfLine selects to the start of the current line -func (a *BufActionHandler) SelectToStartOfLine() bool { +func (a *BufHandler) SelectToStartOfLine() bool { return true } // SelectToEndOfLine selects to the end of the current line -func (a *BufActionHandler) SelectToEndOfLine() bool { +func (a *BufHandler) SelectToEndOfLine() bool { return true } // ParagraphPrevious moves the cursor to the previous empty line, or beginning of the buffer if there's none -func (a *BufActionHandler) ParagraphPrevious() bool { +func (a *BufHandler) ParagraphPrevious() bool { return true } // ParagraphNext moves the cursor to the next empty line, or end of the buffer if there's none -func (a *BufActionHandler) ParagraphNext() bool { +func (a *BufHandler) ParagraphNext() bool { return true } // Retab changes all tabs to spaces or all spaces to tabs depending // on the user's settings -func (a *BufActionHandler) Retab() bool { +func (a *BufHandler) Retab() bool { return true } // CursorStart moves the cursor to the start of the buffer -func (a *BufActionHandler) CursorStart() bool { +func (a *BufHandler) CursorStart() bool { return true } // CursorEnd moves the cursor to the end of the buffer -func (a *BufActionHandler) CursorEnd() bool { +func (a *BufHandler) CursorEnd() bool { return true } // SelectToStart selects the text from the cursor to the start of the buffer -func (a *BufActionHandler) SelectToStart() bool { +func (a *BufHandler) SelectToStart() bool { return true } // SelectToEnd selects the text from the cursor to the end of the buffer -func (a *BufActionHandler) SelectToEnd() bool { +func (a *BufHandler) SelectToEnd() bool { return true } // InsertSpace inserts a space -func (a *BufActionHandler) InsertSpace() bool { +func (a *BufHandler) InsertSpace() bool { return true } // InsertNewline inserts a newline plus possible some whitespace if autoindent is on -func (a *BufActionHandler) InsertNewline() bool { +func (a *BufHandler) InsertNewline() bool { return true } // Backspace deletes the previous character -func (a *BufActionHandler) Backspace() bool { +func (a *BufHandler) Backspace() bool { return true } // DeleteWordRight deletes the word to the right of the cursor -func (a *BufActionHandler) DeleteWordRight() bool { +func (a *BufHandler) DeleteWordRight() bool { return true } // DeleteWordLeft deletes the word to the left of the cursor -func (a *BufActionHandler) DeleteWordLeft() bool { +func (a *BufHandler) DeleteWordLeft() bool { return true } // Delete deletes the next character -func (a *BufActionHandler) Delete() bool { +func (a *BufHandler) Delete() bool { return true } // IndentSelection indents the current selection -func (a *BufActionHandler) IndentSelection() bool { +func (a *BufHandler) IndentSelection() bool { return false } // OutdentLine moves the current line back one indentation -func (a *BufActionHandler) OutdentLine() bool { +func (a *BufHandler) OutdentLine() bool { return true } // OutdentSelection takes the current selection and moves it back one indent level -func (a *BufActionHandler) OutdentSelection() bool { +func (a *BufHandler) OutdentSelection() bool { return false } // InsertTab inserts a tab or spaces -func (a *BufActionHandler) InsertTab() bool { +func (a *BufHandler) InsertTab() bool { return true } // SaveAll saves all open buffers -func (a *BufActionHandler) SaveAll() bool { +func (a *BufHandler) SaveAll() bool { return false } // Save the buffer to disk -func (a *BufActionHandler) Save() bool { +func (a *BufHandler) Save() bool { return false } // SaveAs saves the buffer to disk with the given name -func (a *BufActionHandler) SaveAs() bool { +func (a *BufHandler) SaveAs() bool { return false } // Find opens a prompt and searches forward for the input -func (a *BufActionHandler) Find() bool { +func (a *BufHandler) Find() bool { return true } // FindNext searches forwards for the last used search term -func (a *BufActionHandler) FindNext() bool { +func (a *BufHandler) FindNext() bool { return true } // FindPrevious searches backwards for the last used search term -func (a *BufActionHandler) FindPrevious() bool { +func (a *BufHandler) FindPrevious() bool { return true } // Undo undoes the last action -func (a *BufActionHandler) Undo() bool { +func (a *BufHandler) Undo() bool { return true } // Redo redoes the last action -func (a *BufActionHandler) Redo() bool { +func (a *BufHandler) Redo() bool { return true } // Copy the selection to the system clipboard -func (a *BufActionHandler) Copy() bool { +func (a *BufHandler) Copy() bool { return true } // CutLine cuts the current line to the clipboard -func (a *BufActionHandler) CutLine() bool { +func (a *BufHandler) CutLine() bool { return true } // Cut the selection to the system clipboard -func (a *BufActionHandler) Cut() bool { +func (a *BufHandler) Cut() bool { return true } // DuplicateLine duplicates the current line or selection -func (a *BufActionHandler) DuplicateLine() bool { +func (a *BufHandler) DuplicateLine() bool { return true } // DeleteLine deletes the current line -func (a *BufActionHandler) DeleteLine() bool { +func (a *BufHandler) DeleteLine() bool { return true } // MoveLinesUp moves up the current line or selected lines if any -func (a *BufActionHandler) MoveLinesUp() bool { +func (a *BufHandler) MoveLinesUp() bool { return true } // MoveLinesDown moves down the current line or selected lines if any -func (a *BufActionHandler) MoveLinesDown() bool { +func (a *BufHandler) MoveLinesDown() bool { return true } // Paste whatever is in the system clipboard into the buffer // Delete and paste if the user has a selection -func (a *BufActionHandler) Paste() bool { +func (a *BufHandler) Paste() bool { return true } // PastePrimary pastes from the primary clipboard (only use on linux) -func (a *BufActionHandler) PastePrimary() bool { +func (a *BufHandler) PastePrimary() bool { return true } // JumpToMatchingBrace moves the cursor to the matching brace if it is // currently on a brace -func (a *BufActionHandler) JumpToMatchingBrace() bool { +func (a *BufHandler) JumpToMatchingBrace() bool { return true } // SelectAll selects the entire buffer -func (a *BufActionHandler) SelectAll() bool { +func (a *BufHandler) SelectAll() bool { return true } // OpenFile opens a new file in the buffer -func (a *BufActionHandler) OpenFile() bool { +func (a *BufHandler) OpenFile() bool { return false } // Start moves the viewport to the start of the buffer -func (a *BufActionHandler) Start() bool { +func (a *BufHandler) Start() bool { return false } // End moves the viewport to the end of the buffer -func (a *BufActionHandler) End() bool { +func (a *BufHandler) End() bool { return false } // PageUp scrolls the view up a page -func (a *BufActionHandler) PageUp() bool { +func (a *BufHandler) PageUp() bool { return false } // PageDown scrolls the view down a page -func (a *BufActionHandler) PageDown() bool { +func (a *BufHandler) PageDown() bool { return false } // SelectPageUp selects up one page -func (a *BufActionHandler) SelectPageUp() bool { +func (a *BufHandler) SelectPageUp() bool { return true } // SelectPageDown selects down one page -func (a *BufActionHandler) SelectPageDown() bool { +func (a *BufHandler) SelectPageDown() bool { return true } // CursorPageUp places the cursor a page up -func (a *BufActionHandler) CursorPageUp() bool { +func (a *BufHandler) CursorPageUp() bool { return true } // CursorPageDown places the cursor a page up -func (a *BufActionHandler) CursorPageDown() bool { +func (a *BufHandler) CursorPageDown() bool { return true } // HalfPageUp scrolls the view up half a page -func (a *BufActionHandler) HalfPageUp() bool { +func (a *BufHandler) HalfPageUp() bool { return false } // HalfPageDown scrolls the view down half a page -func (a *BufActionHandler) HalfPageDown() bool { +func (a *BufHandler) HalfPageDown() bool { return false } // ToggleRuler turns line numbers off and on -func (a *BufActionHandler) ToggleRuler() bool { +func (a *BufHandler) ToggleRuler() bool { return false } // JumpLine jumps to a line and moves the view accordingly. -func (a *BufActionHandler) JumpLine() bool { +func (a *BufHandler) JumpLine() bool { return false } // ClearStatus clears the messenger bar -func (a *BufActionHandler) ClearStatus() bool { +func (a *BufHandler) ClearStatus() bool { return false } // ToggleHelp toggles the help screen -func (a *BufActionHandler) ToggleHelp() bool { +func (a *BufHandler) ToggleHelp() bool { return true } // ToggleKeyMenu toggles the keymenu option and resizes all tabs -func (a *BufActionHandler) ToggleKeyMenu() bool { +func (a *BufHandler) ToggleKeyMenu() bool { return true } // ShellMode opens a terminal to run a shell command -func (a *BufActionHandler) ShellMode() bool { +func (a *BufHandler) ShellMode() bool { return false } // CommandMode lets the user enter a command -func (a *BufActionHandler) CommandMode() bool { +func (a *BufHandler) CommandMode() bool { return false } // ToggleOverwriteMode lets the user toggle the text overwrite mode -func (a *BufActionHandler) ToggleOverwriteMode() bool { +func (a *BufHandler) ToggleOverwriteMode() bool { return false } // Escape leaves current mode -func (a *BufActionHandler) Escape() bool { +func (a *BufHandler) Escape() bool { return false } // Quit this will close the current tab or view that is open -func (a *BufActionHandler) Quit() bool { +func (a *BufHandler) Quit() bool { screen.Screen.Fini() os.Exit(0) return false } // QuitAll quits the whole editor; all splits and tabs -func (a *BufActionHandler) QuitAll() bool { +func (a *BufHandler) QuitAll() bool { return false } // AddTab adds a new tab with an empty buffer -func (a *BufActionHandler) AddTab() bool { +func (a *BufHandler) AddTab() bool { return true } // PreviousTab switches to the previous tab in the tab list -func (a *BufActionHandler) PreviousTab() bool { +func (a *BufHandler) PreviousTab() bool { return false } // NextTab switches to the next tab in the tab list -func (a *BufActionHandler) NextTab() bool { +func (a *BufHandler) NextTab() bool { return false } // VSplitBinding opens an empty vertical split -func (a *BufActionHandler) VSplitBinding() bool { +func (a *BufHandler) VSplitBinding() bool { return false } // HSplitBinding opens an empty horizontal split -func (a *BufActionHandler) HSplitBinding() bool { +func (a *BufHandler) HSplitBinding() bool { return false } // Unsplit closes all splits in the current tab except the active one -func (a *BufActionHandler) Unsplit() bool { +func (a *BufHandler) Unsplit() bool { return false } // NextSplit changes the view to the next split -func (a *BufActionHandler) NextSplit() bool { +func (a *BufHandler) NextSplit() bool { return false } // PreviousSplit changes the view to the previous split -func (a *BufActionHandler) PreviousSplit() bool { +func (a *BufHandler) PreviousSplit() bool { return false } @@ -460,41 +460,41 @@ var curMacro []interface{} var recordingMacro bool // ToggleMacro toggles recording of a macro -func (a *BufActionHandler) ToggleMacro() bool { +func (a *BufHandler) ToggleMacro() bool { return true } // PlayMacro plays back the most recently recorded macro -func (a *BufActionHandler) PlayMacro() bool { +func (a *BufHandler) PlayMacro() bool { return true } // SpawnMultiCursor creates a new multiple cursor at the next occurrence of the current selection or current word -func (a *BufActionHandler) SpawnMultiCursor() bool { +func (a *BufHandler) SpawnMultiCursor() bool { return false } // SpawnMultiCursorSelect adds a cursor at the beginning of each line of a selection -func (a *BufActionHandler) SpawnMultiCursorSelect() bool { +func (a *BufHandler) SpawnMultiCursorSelect() bool { return false } // MouseMultiCursor is a mouse action which puts a new cursor at the mouse position -func (a *BufActionHandler) MouseMultiCursor(e *tcell.EventMouse) bool { +func (a *BufHandler) MouseMultiCursor(e *tcell.EventMouse) bool { return false } // SkipMultiCursor moves the current multiple cursor to the next available position -func (a *BufActionHandler) SkipMultiCursor() bool { +func (a *BufHandler) SkipMultiCursor() bool { return false } // RemoveMultiCursor removes the latest multiple cursor -func (a *BufActionHandler) RemoveMultiCursor() bool { +func (a *BufHandler) RemoveMultiCursor() bool { return false } // RemoveAllMultiCursors removes all cursors except the base cursor -func (a *BufActionHandler) RemoveAllMultiCursors() bool { +func (a *BufHandler) RemoveAllMultiCursors() bool { return false } diff --git a/cmd/micro/actions_other.go b/cmd/micro/action/actions_other.go similarity index 56% rename from cmd/micro/actions_other.go rename to cmd/micro/action/actions_other.go index 77c53ebf..15699895 100644 --- a/cmd/micro/actions_other.go +++ b/cmd/micro/action/actions_other.go @@ -1,8 +1,8 @@ // +build plan9 nacl windows -package main +package action -func (*BufActionHandler) Suspend() bool { +func (*BufHandler) Suspend() bool { // TODO: error message return false } diff --git a/cmd/micro/actions_posix.go b/cmd/micro/action/actions_posix.go similarity index 92% rename from cmd/micro/actions_posix.go rename to cmd/micro/action/actions_posix.go index b9070810..8981685f 100644 --- a/cmd/micro/actions_posix.go +++ b/cmd/micro/action/actions_posix.go @@ -1,6 +1,6 @@ // +build linux darwin dragonfly solaris openbsd netbsd freebsd -package main +package action import ( "syscall" @@ -12,7 +12,7 @@ import ( // Suspend sends micro to the background. This is the same as pressing CtrlZ in most unix programs. // This only works on linux and has no default binding. // This code was adapted from the suspend code in nsf/godit -func (*BufActionHandler) Suspend() bool { +func (*BufHandler) Suspend() bool { screenWasNil := screen.Screen == nil if !screenWasNil { diff --git a/cmd/micro/bindings.go b/cmd/micro/action/bindings.go similarity index 99% rename from cmd/micro/bindings.go rename to cmd/micro/action/bindings.go index 56989383..816b14ae 100644 --- a/cmd/micro/bindings.go +++ b/cmd/micro/action/bindings.go @@ -1,4 +1,4 @@ -package main +package action import ( "encoding/json" @@ -14,7 +14,7 @@ import ( "github.com/zyedidia/tcell" ) -var bindings = DefaultBindings() +var Bindings = DefaultBindings() func InitBindings() { var parsed map[string]string @@ -57,7 +57,7 @@ func BindKey(k, v string) { util.TermMessage("Raw events not supported yet") } - bindings[k] = v + Bindings[k] = v } // findKeyEvent will find binding Key 'b' using string 'k' diff --git a/cmd/micro/action/bufhandler.go b/cmd/micro/action/bufhandler.go new file mode 100644 index 00000000..e6951a2f --- /dev/null +++ b/cmd/micro/action/bufhandler.go @@ -0,0 +1,206 @@ +package action + +import ( + "time" + + "github.com/zyedidia/micro/cmd/micro/buffer" + "github.com/zyedidia/tcell" +) + +type BufKeyAction func(*BufHandler) bool +type BufMouseAction func(*BufHandler, *tcell.EventMouse) bool + +var BufKeyBindings map[KeyEvent]BufKeyAction +var BufMouseBindings map[MouseEvent]BufMouseAction + +func init() { + BufKeyBindings = make(map[KeyEvent]BufKeyAction) + BufMouseBindings = make(map[MouseEvent]BufMouseAction) +} + +func BufMapKey(k KeyEvent, action string) { + BufKeyBindings[k] = BufKeyActions[action] +} +func BufMapMouse(k MouseEvent, action string) { + BufMouseBindings[k] = BufMouseActions[action] +} + +// The BufHandler connects the buffer and the window +// It provides a cursor (or multiple) and defines a set of actions +// that can be taken on the buffer +// The ActionHandler can access the window for necessary info about +// visual positions for mouse clicks and scrolling +type BufHandler struct { + Buf *buffer.Buffer + + cursors []*buffer.Cursor + Cursor *buffer.Cursor // the active cursor + + // Since tcell doesn't differentiate between a mouse release event + // and a mouse move event with no keys pressed, we need to keep + // track of whether or not the mouse was pressed (or not released) last event to determine + // mouse release events + mouseReleased bool + + // We need to keep track of insert key press toggle + isOverwriteMode bool + // This stores when the last click was + // This is useful for detecting double and triple clicks + lastClickTime time.Time + lastLoc buffer.Loc + + // lastCutTime stores when the last ctrl+k was issued. + // It is used for clearing the clipboard to replace it with fresh cut lines. + lastCutTime time.Time + + // freshClip returns true if the clipboard has never been pasted. + freshClip bool + + // Was the last mouse event actually a double click? + // Useful for detecting triple clicks -- if a double click is detected + // but the last mouse event was actually a double click, it's a triple click + doubleClick bool + // Same here, just to keep track for mouse move events + tripleClick bool +} + +func NewBufHandler(buf *buffer.Buffer) *BufHandler { + a := new(BufHandler) + a.Buf = buf + + a.cursors = []*buffer.Cursor{&buffer.Cursor{ + Buf: buf, + Loc: buf.StartCursor, + }} + a.Cursor = a.cursors[0] + + buf.SetCursors(a.cursors) + return a +} + +// HandleEvent executes the tcell event properly +// TODO: multiple actions bound to one key +func (a *BufHandler) HandleEvent(event tcell.Event) { + switch e := event.(type) { + case *tcell.EventKey: + ke := KeyEvent{ + code: e.Key(), + mod: e.Modifiers(), + r: e.Rune(), + } + if action, ok := BufKeyBindings[ke]; ok { + action(a) + } + case *tcell.EventMouse: + me := MouseEvent{ + btn: e.Buttons(), + mod: e.Modifiers(), + } + if action, ok := BufMouseBindings[me]; ok { + action(a, e) + } + } +} + +var BufKeyActions = map[string]BufKeyAction{ + "CursorUp": (*BufHandler).CursorUp, + "CursorDown": (*BufHandler).CursorDown, + "CursorPageUp": (*BufHandler).CursorPageUp, + "CursorPageDown": (*BufHandler).CursorPageDown, + "CursorLeft": (*BufHandler).CursorLeft, + "CursorRight": (*BufHandler).CursorRight, + "CursorStart": (*BufHandler).CursorStart, + "CursorEnd": (*BufHandler).CursorEnd, + "SelectToStart": (*BufHandler).SelectToStart, + "SelectToEnd": (*BufHandler).SelectToEnd, + "SelectUp": (*BufHandler).SelectUp, + "SelectDown": (*BufHandler).SelectDown, + "SelectLeft": (*BufHandler).SelectLeft, + "SelectRight": (*BufHandler).SelectRight, + "WordRight": (*BufHandler).WordRight, + "WordLeft": (*BufHandler).WordLeft, + "SelectWordRight": (*BufHandler).SelectWordRight, + "SelectWordLeft": (*BufHandler).SelectWordLeft, + "DeleteWordRight": (*BufHandler).DeleteWordRight, + "DeleteWordLeft": (*BufHandler).DeleteWordLeft, + "SelectLine": (*BufHandler).SelectLine, + "SelectToStartOfLine": (*BufHandler).SelectToStartOfLine, + "SelectToEndOfLine": (*BufHandler).SelectToEndOfLine, + "ParagraphPrevious": (*BufHandler).ParagraphPrevious, + "ParagraphNext": (*BufHandler).ParagraphNext, + "InsertNewline": (*BufHandler).InsertNewline, + "InsertSpace": (*BufHandler).InsertSpace, + "Backspace": (*BufHandler).Backspace, + "Delete": (*BufHandler).Delete, + "InsertTab": (*BufHandler).InsertTab, + "Save": (*BufHandler).Save, + "SaveAll": (*BufHandler).SaveAll, + "SaveAs": (*BufHandler).SaveAs, + "Find": (*BufHandler).Find, + "FindNext": (*BufHandler).FindNext, + "FindPrevious": (*BufHandler).FindPrevious, + "Center": (*BufHandler).Center, + "Undo": (*BufHandler).Undo, + "Redo": (*BufHandler).Redo, + "Copy": (*BufHandler).Copy, + "Cut": (*BufHandler).Cut, + "CutLine": (*BufHandler).CutLine, + "DuplicateLine": (*BufHandler).DuplicateLine, + "DeleteLine": (*BufHandler).DeleteLine, + "MoveLinesUp": (*BufHandler).MoveLinesUp, + "MoveLinesDown": (*BufHandler).MoveLinesDown, + "IndentSelection": (*BufHandler).IndentSelection, + "OutdentSelection": (*BufHandler).OutdentSelection, + "OutdentLine": (*BufHandler).OutdentLine, + "Paste": (*BufHandler).Paste, + "PastePrimary": (*BufHandler).PastePrimary, + "SelectAll": (*BufHandler).SelectAll, + "OpenFile": (*BufHandler).OpenFile, + "Start": (*BufHandler).Start, + "End": (*BufHandler).End, + "PageUp": (*BufHandler).PageUp, + "PageDown": (*BufHandler).PageDown, + "SelectPageUp": (*BufHandler).SelectPageUp, + "SelectPageDown": (*BufHandler).SelectPageDown, + "HalfPageUp": (*BufHandler).HalfPageUp, + "HalfPageDown": (*BufHandler).HalfPageDown, + "StartOfLine": (*BufHandler).StartOfLine, + "EndOfLine": (*BufHandler).EndOfLine, + "ToggleHelp": (*BufHandler).ToggleHelp, + "ToggleKeyMenu": (*BufHandler).ToggleKeyMenu, + "ToggleRuler": (*BufHandler).ToggleRuler, + "JumpLine": (*BufHandler).JumpLine, + "ClearStatus": (*BufHandler).ClearStatus, + "ShellMode": (*BufHandler).ShellMode, + "CommandMode": (*BufHandler).CommandMode, + "ToggleOverwriteMode": (*BufHandler).ToggleOverwriteMode, + "Escape": (*BufHandler).Escape, + "Quit": (*BufHandler).Quit, + "QuitAll": (*BufHandler).QuitAll, + "AddTab": (*BufHandler).AddTab, + "PreviousTab": (*BufHandler).PreviousTab, + "NextTab": (*BufHandler).NextTab, + "NextSplit": (*BufHandler).NextSplit, + "PreviousSplit": (*BufHandler).PreviousSplit, + "Unsplit": (*BufHandler).Unsplit, + "VSplit": (*BufHandler).VSplitBinding, + "HSplit": (*BufHandler).HSplitBinding, + "ToggleMacro": (*BufHandler).ToggleMacro, + "PlayMacro": (*BufHandler).PlayMacro, + "Suspend": (*BufHandler).Suspend, + "ScrollUp": (*BufHandler).ScrollUpAction, + "ScrollDown": (*BufHandler).ScrollDownAction, + "SpawnMultiCursor": (*BufHandler).SpawnMultiCursor, + "SpawnMultiCursorSelect": (*BufHandler).SpawnMultiCursorSelect, + "RemoveMultiCursor": (*BufHandler).RemoveMultiCursor, + "RemoveAllMultiCursors": (*BufHandler).RemoveAllMultiCursors, + "SkipMultiCursor": (*BufHandler).SkipMultiCursor, + "JumpToMatchingBrace": (*BufHandler).JumpToMatchingBrace, + + // This was changed to InsertNewline but I don't want to break backwards compatibility + "InsertEnter": (*BufHandler).InsertNewline, +} +var BufMouseActions = map[string]BufMouseAction{ + "MousePress": (*BufHandler).MousePress, + "MouseMultiCursor": (*BufHandler).MouseMultiCursor, +} diff --git a/cmd/micro/actionhandler.go b/cmd/micro/action/handler.go similarity index 87% rename from cmd/micro/actionhandler.go rename to cmd/micro/action/handler.go index 26bfe753..447fe79e 100644 --- a/cmd/micro/actionhandler.go +++ b/cmd/micro/action/handler.go @@ -1,4 +1,4 @@ -package main +package action import ( "github.com/zyedidia/tcell" @@ -31,8 +31,8 @@ type MouseEvent struct { mod tcell.ModMask } -// An ActionHandler will take a tcell event and execute it +// A Handler will take a tcell event and execute it // appropriately -type ActionHandler interface { +type Handler interface { HandleEvent(tcell.Event) } diff --git a/cmd/micro/bufactionhandler.go b/cmd/micro/bufactionhandler.go deleted file mode 100644 index 839c39b7..00000000 --- a/cmd/micro/bufactionhandler.go +++ /dev/null @@ -1,208 +0,0 @@ -package main - -import ( - "time" - - "github.com/zyedidia/micro/cmd/micro/buffer" - "github.com/zyedidia/tcell" -) - -type BufKeyAction func(*BufActionHandler) bool -type BufMouseAction func(*BufActionHandler, *tcell.EventMouse) bool - -var BufKeyBindings map[KeyEvent]BufKeyAction -var BufMouseBindings map[MouseEvent]BufMouseAction - -func init() { - BufKeyBindings = make(map[KeyEvent]BufKeyAction) - BufMouseBindings = make(map[MouseEvent]BufMouseAction) -} - -func BufMapKey(k KeyEvent, action string) { - BufKeyBindings[k] = BufKeyActions[action] -} -func BufMapMouse(k MouseEvent, action string) { - BufMouseBindings[k] = BufMouseActions[action] -} - -// The BufActionHandler connects the buffer and the window -// It provides a cursor (or multiple) and defines a set of actions -// that can be taken on the buffer -// The ActionHandler can access the window for necessary info about -// visual positions for mouse clicks and scrolling -type BufActionHandler struct { - Buf *buffer.Buffer - Win *Window - - cursors []*buffer.Cursor - Cursor *buffer.Cursor // the active cursor - - // Since tcell doesn't differentiate between a mouse release event - // and a mouse move event with no keys pressed, we need to keep - // track of whether or not the mouse was pressed (or not released) last event to determine - // mouse release events - mouseReleased bool - - // We need to keep track of insert key press toggle - isOverwriteMode bool - // This stores when the last click was - // This is useful for detecting double and triple clicks - lastClickTime time.Time - lastLoc buffer.Loc - - // lastCutTime stores when the last ctrl+k was issued. - // It is used for clearing the clipboard to replace it with fresh cut lines. - lastCutTime time.Time - - // freshClip returns true if the clipboard has never been pasted. - freshClip bool - - // Was the last mouse event actually a double click? - // Useful for detecting triple clicks -- if a double click is detected - // but the last mouse event was actually a double click, it's a triple click - doubleClick bool - // Same here, just to keep track for mouse move events - tripleClick bool -} - -func NewBufActionHandler(buf *buffer.Buffer, win *Window) *BufActionHandler { - a := new(BufActionHandler) - a.Buf = buf - a.Win = win - - a.cursors = []*buffer.Cursor{&buffer.Cursor{ - Buf: buf, - Loc: buf.StartCursor, - }} - a.Cursor = a.cursors[0] - - buf.SetCursors(a.cursors) - return a -} - -// HandleEvent executes the tcell event properly -// TODO: multiple actions bound to one key -func (a *BufActionHandler) HandleEvent(event tcell.Event) { - switch e := event.(type) { - case *tcell.EventKey: - ke := KeyEvent{ - code: e.Key(), - mod: e.Modifiers(), - r: e.Rune(), - } - if action, ok := BufKeyBindings[ke]; ok { - action(a) - } - case *tcell.EventMouse: - me := MouseEvent{ - btn: e.Buttons(), - mod: e.Modifiers(), - } - if action, ok := BufMouseBindings[me]; ok { - action(a, e) - } - } -} - -var BufKeyActions = map[string]BufKeyAction{ - "CursorUp": (*BufActionHandler).CursorUp, - "CursorDown": (*BufActionHandler).CursorDown, - "CursorPageUp": (*BufActionHandler).CursorPageUp, - "CursorPageDown": (*BufActionHandler).CursorPageDown, - "CursorLeft": (*BufActionHandler).CursorLeft, - "CursorRight": (*BufActionHandler).CursorRight, - "CursorStart": (*BufActionHandler).CursorStart, - "CursorEnd": (*BufActionHandler).CursorEnd, - "SelectToStart": (*BufActionHandler).SelectToStart, - "SelectToEnd": (*BufActionHandler).SelectToEnd, - "SelectUp": (*BufActionHandler).SelectUp, - "SelectDown": (*BufActionHandler).SelectDown, - "SelectLeft": (*BufActionHandler).SelectLeft, - "SelectRight": (*BufActionHandler).SelectRight, - "WordRight": (*BufActionHandler).WordRight, - "WordLeft": (*BufActionHandler).WordLeft, - "SelectWordRight": (*BufActionHandler).SelectWordRight, - "SelectWordLeft": (*BufActionHandler).SelectWordLeft, - "DeleteWordRight": (*BufActionHandler).DeleteWordRight, - "DeleteWordLeft": (*BufActionHandler).DeleteWordLeft, - "SelectLine": (*BufActionHandler).SelectLine, - "SelectToStartOfLine": (*BufActionHandler).SelectToStartOfLine, - "SelectToEndOfLine": (*BufActionHandler).SelectToEndOfLine, - "ParagraphPrevious": (*BufActionHandler).ParagraphPrevious, - "ParagraphNext": (*BufActionHandler).ParagraphNext, - "InsertNewline": (*BufActionHandler).InsertNewline, - "InsertSpace": (*BufActionHandler).InsertSpace, - "Backspace": (*BufActionHandler).Backspace, - "Delete": (*BufActionHandler).Delete, - "InsertTab": (*BufActionHandler).InsertTab, - "Save": (*BufActionHandler).Save, - "SaveAll": (*BufActionHandler).SaveAll, - "SaveAs": (*BufActionHandler).SaveAs, - "Find": (*BufActionHandler).Find, - "FindNext": (*BufActionHandler).FindNext, - "FindPrevious": (*BufActionHandler).FindPrevious, - "Center": (*BufActionHandler).Center, - "Undo": (*BufActionHandler).Undo, - "Redo": (*BufActionHandler).Redo, - "Copy": (*BufActionHandler).Copy, - "Cut": (*BufActionHandler).Cut, - "CutLine": (*BufActionHandler).CutLine, - "DuplicateLine": (*BufActionHandler).DuplicateLine, - "DeleteLine": (*BufActionHandler).DeleteLine, - "MoveLinesUp": (*BufActionHandler).MoveLinesUp, - "MoveLinesDown": (*BufActionHandler).MoveLinesDown, - "IndentSelection": (*BufActionHandler).IndentSelection, - "OutdentSelection": (*BufActionHandler).OutdentSelection, - "OutdentLine": (*BufActionHandler).OutdentLine, - "Paste": (*BufActionHandler).Paste, - "PastePrimary": (*BufActionHandler).PastePrimary, - "SelectAll": (*BufActionHandler).SelectAll, - "OpenFile": (*BufActionHandler).OpenFile, - "Start": (*BufActionHandler).Start, - "End": (*BufActionHandler).End, - "PageUp": (*BufActionHandler).PageUp, - "PageDown": (*BufActionHandler).PageDown, - "SelectPageUp": (*BufActionHandler).SelectPageUp, - "SelectPageDown": (*BufActionHandler).SelectPageDown, - "HalfPageUp": (*BufActionHandler).HalfPageUp, - "HalfPageDown": (*BufActionHandler).HalfPageDown, - "StartOfLine": (*BufActionHandler).StartOfLine, - "EndOfLine": (*BufActionHandler).EndOfLine, - "ToggleHelp": (*BufActionHandler).ToggleHelp, - "ToggleKeyMenu": (*BufActionHandler).ToggleKeyMenu, - "ToggleRuler": (*BufActionHandler).ToggleRuler, - "JumpLine": (*BufActionHandler).JumpLine, - "ClearStatus": (*BufActionHandler).ClearStatus, - "ShellMode": (*BufActionHandler).ShellMode, - "CommandMode": (*BufActionHandler).CommandMode, - "ToggleOverwriteMode": (*BufActionHandler).ToggleOverwriteMode, - "Escape": (*BufActionHandler).Escape, - "Quit": (*BufActionHandler).Quit, - "QuitAll": (*BufActionHandler).QuitAll, - "AddTab": (*BufActionHandler).AddTab, - "PreviousTab": (*BufActionHandler).PreviousTab, - "NextTab": (*BufActionHandler).NextTab, - "NextSplit": (*BufActionHandler).NextSplit, - "PreviousSplit": (*BufActionHandler).PreviousSplit, - "Unsplit": (*BufActionHandler).Unsplit, - "VSplit": (*BufActionHandler).VSplitBinding, - "HSplit": (*BufActionHandler).HSplitBinding, - "ToggleMacro": (*BufActionHandler).ToggleMacro, - "PlayMacro": (*BufActionHandler).PlayMacro, - "Suspend": (*BufActionHandler).Suspend, - "ScrollUp": (*BufActionHandler).ScrollUpAction, - "ScrollDown": (*BufActionHandler).ScrollDownAction, - "SpawnMultiCursor": (*BufActionHandler).SpawnMultiCursor, - "SpawnMultiCursorSelect": (*BufActionHandler).SpawnMultiCursorSelect, - "RemoveMultiCursor": (*BufActionHandler).RemoveMultiCursor, - "RemoveAllMultiCursors": (*BufActionHandler).RemoveAllMultiCursors, - "SkipMultiCursor": (*BufActionHandler).SkipMultiCursor, - "JumpToMatchingBrace": (*BufActionHandler).JumpToMatchingBrace, - - // This was changed to InsertNewline but I don't want to break backwards compatibility - "InsertEnter": (*BufActionHandler).InsertNewline, -} -var BufMouseActions = map[string]BufMouseAction{ - "MousePress": (*BufActionHandler).MousePress, - "MouseMultiCursor": (*BufActionHandler).MouseMultiCursor, -} diff --git a/cmd/micro/buffer/buffer.go b/cmd/micro/buffer/buffer.go index e3118eb9..8f256c2f 100644 --- a/cmd/micro/buffer/buffer.go +++ b/cmd/micro/buffer/buffer.go @@ -2,15 +2,11 @@ package buffer import ( "bufio" - "bytes" "crypto/md5" - "encoding/gob" "errors" "io" "io/ioutil" "os" - "os/exec" - "os/signal" "path/filepath" "strings" "time" @@ -236,154 +232,6 @@ func (b *Buffer) ReOpen() error { // b.Cursor.Relocate() } -// Saving - -// Save saves the buffer to its default path -func (b *Buffer) Save() error { - return b.SaveAs(b.Path) -} - -// SaveAs saves the buffer to a specified path (filename), creating the file if it does not exist -func (b *Buffer) SaveAs(filename string) error { - // TODO: rmtrailingws and updaterules - b.UpdateRules() - // if b.Settings["rmtrailingws"].(bool) { - // for i, l := range b.lines { - // pos := len(bytes.TrimRightFunc(l.data, unicode.IsSpace)) - // - // if pos < len(l.data) { - // b.deleteToEnd(Loc{pos, i}) - // } - // } - // - // b.Cursor.Relocate() - // } - - if b.Settings["eofnewline"].(bool) { - end := b.End() - if b.RuneAt(Loc{end.X - 1, end.Y}) != '\n' { - b.Insert(end, "\n") - } - } - - // Update the last time this file was updated after saving - defer func() { - b.ModTime, _ = GetModTime(filename) - }() - - // Removes any tilde and replaces with the absolute path to home - absFilename, _ := ReplaceHome(filename) - - // TODO: save creates parent dirs - // // Get the leading path to the file | "." is returned if there's no leading path provided - // if dirname := filepath.Dir(absFilename); dirname != "." { - // // Check if the parent dirs don't exist - // if _, statErr := os.Stat(dirname); os.IsNotExist(statErr) { - // // Prompt to make sure they want to create the dirs that are missing - // if yes, canceled := messenger.YesNoPrompt("Parent folders \"" + dirname + "\" do not exist. Create them? (y,n)"); yes && !canceled { - // // Create all leading dir(s) since they don't exist - // if mkdirallErr := os.MkdirAll(dirname, os.ModePerm); mkdirallErr != nil { - // // If there was an error creating the dirs - // return mkdirallErr - // } - // } else { - // // If they canceled the creation of leading dirs - // return errors.New("Save aborted") - // } - // } - // } - - var fileSize int - - err := overwriteFile(absFilename, func(file io.Writer) (e error) { - if len(b.lines) == 0 { - return - } - - // end of line - var eol []byte - if b.Settings["fileformat"] == "dos" { - eol = []byte{'\r', '\n'} - } else { - eol = []byte{'\n'} - } - - // write lines - if fileSize, e = file.Write(b.lines[0].data); e != nil { - return - } - - for _, l := range b.lines[1:] { - if _, e = file.Write(eol); e != nil { - return - } - if _, e = file.Write(l.data); e != nil { - return - } - fileSize += len(eol) + len(l.data) - } - return - }) - - if err != nil { - return err - } - - if !b.Settings["fastdirty"].(bool) { - if fileSize > LargeFileThreshold { - // For large files 'fastdirty' needs to be on - b.Settings["fastdirty"] = true - } else { - calcHash(b, &b.origHash) - } - } - - b.Path = filename - absPath, _ := filepath.Abs(filename) - b.AbsPath = absPath - b.isModified = false - return b.Serialize() -} - -// SaveWithSudo saves the buffer to the default path with sudo -func (b *Buffer) SaveWithSudo() error { - return b.SaveAsWithSudo(b.Path) -} - -// SaveAsWithSudo is the same as SaveAs except it uses a neat trick -// with tee to use sudo so the user doesn't have to reopen micro with sudo -func (b *Buffer) SaveAsWithSudo(filename string) error { - b.UpdateRules() - b.Path = filename - absPath, _ := filepath.Abs(filename) - b.AbsPath = absPath - - // Set up everything for the command - cmd := exec.Command(config.GlobalSettings["sucmd"].(string), "tee", filename) - cmd.Stdin = bytes.NewBuffer(b.Bytes()) - - // 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() { - for range c { - cmd.Process.Kill() - } - }() - - // Start the command - cmd.Start() - err := cmd.Wait() - - if err == nil { - b.isModified = false - b.ModTime, _ = GetModTime(filename) - return b.Serialize() - } - return err -} - func (b *Buffer) SetCursors(c []*Cursor) { b.cursors = c } @@ -472,56 +320,6 @@ func calcHash(b *Buffer, out *[md5.Size]byte) { h.Sum((*out)[:0]) } -func init() { - gob.Register(TextEvent{}) - gob.Register(SerializedBuffer{}) -} - -// Serialize serializes the buffer to config.ConfigDir/buffers -func (b *Buffer) Serialize() error { - if !b.Settings["savecursor"].(bool) && !b.Settings["saveundo"].(bool) { - return nil - } - - name := config.ConfigDir + "/buffers/" + EscapePath(b.AbsPath) - - return overwriteFile(name, func(file io.Writer) error { - return gob.NewEncoder(file).Encode(SerializedBuffer{ - b.EventHandler, - b.GetActiveCursor().Loc, - b.ModTime, - }) - }) -} - -func (b *Buffer) Unserialize() error { - // If either savecursor or saveundo is turned on, we need to load the serialized information - // from ~/.config/micro/buffers - file, err := os.Open(config.ConfigDir + "/buffers/" + EscapePath(b.AbsPath)) - defer file.Close() - if err == nil { - var buffer SerializedBuffer - decoder := gob.NewDecoder(file) - gob.Register(TextEvent{}) - err = decoder.Decode(&buffer) - if err != nil { - return errors.New(err.Error() + "\nYou may want to remove the files in ~/.config/micro/buffers (these files store the information for the 'saveundo' and 'savecursor' options) if this problem persists.") - } - if b.Settings["savecursor"].(bool) { - b.StartCursor = buffer.Cursor - } - - if b.Settings["saveundo"].(bool) { - // We should only use last time's eventhandler if the file wasn't modified by someone else in the meantime - if b.ModTime == buffer.ModTime { - b.EventHandler = buffer.EventHandler - b.EventHandler.buf = b - } - } - } - return err -} - // UpdateRules updates the syntax rules and filetype for this buffer // This is called when the colorscheme changes func (b *Buffer) UpdateRules() { diff --git a/cmd/micro/buffer/save.go b/cmd/micro/buffer/save.go new file mode 100644 index 00000000..a2270434 --- /dev/null +++ b/cmd/micro/buffer/save.go @@ -0,0 +1,160 @@ +package buffer + +import ( + "bytes" + "io" + "os" + "os/exec" + "os/signal" + "path/filepath" + + . "github.com/zyedidia/micro/cmd/micro/util" + + "github.com/zyedidia/micro/cmd/micro/config" +) + +// Save saves the buffer to its default path +func (b *Buffer) Save() error { + return b.SaveAs(b.Path) +} + +// SaveAs saves the buffer to a specified path (filename), creating the file if it does not exist +func (b *Buffer) SaveAs(filename string) error { + // TODO: rmtrailingws and updaterules + b.UpdateRules() + // if b.Settings["rmtrailingws"].(bool) { + // for i, l := range b.lines { + // pos := len(bytes.TrimRightFunc(l.data, unicode.IsSpace)) + // + // if pos < len(l.data) { + // b.deleteToEnd(Loc{pos, i}) + // } + // } + // + // b.Cursor.Relocate() + // } + + if b.Settings["eofnewline"].(bool) { + end := b.End() + if b.RuneAt(Loc{end.X - 1, end.Y}) != '\n' { + b.Insert(end, "\n") + } + } + + // Update the last time this file was updated after saving + defer func() { + b.ModTime, _ = GetModTime(filename) + }() + + // Removes any tilde and replaces with the absolute path to home + absFilename, _ := ReplaceHome(filename) + + // TODO: save creates parent dirs + // // Get the leading path to the file | "." is returned if there's no leading path provided + // if dirname := filepath.Dir(absFilename); dirname != "." { + // // Check if the parent dirs don't exist + // if _, statErr := os.Stat(dirname); os.IsNotExist(statErr) { + // // Prompt to make sure they want to create the dirs that are missing + // if yes, canceled := messenger.YesNoPrompt("Parent folders \"" + dirname + "\" do not exist. Create them? (y,n)"); yes && !canceled { + // // Create all leading dir(s) since they don't exist + // if mkdirallErr := os.MkdirAll(dirname, os.ModePerm); mkdirallErr != nil { + // // If there was an error creating the dirs + // return mkdirallErr + // } + // } else { + // // If they canceled the creation of leading dirs + // return errors.New("Save aborted") + // } + // } + // } + + var fileSize int + + err := overwriteFile(absFilename, func(file io.Writer) (e error) { + if len(b.lines) == 0 { + return + } + + // end of line + var eol []byte + if b.Settings["fileformat"] == "dos" { + eol = []byte{'\r', '\n'} + } else { + eol = []byte{'\n'} + } + + // write lines + if fileSize, e = file.Write(b.lines[0].data); e != nil { + return + } + + for _, l := range b.lines[1:] { + if _, e = file.Write(eol); e != nil { + return + } + if _, e = file.Write(l.data); e != nil { + return + } + fileSize += len(eol) + len(l.data) + } + return + }) + + if err != nil { + return err + } + + if !b.Settings["fastdirty"].(bool) { + if fileSize > LargeFileThreshold { + // For large files 'fastdirty' needs to be on + b.Settings["fastdirty"] = true + } else { + calcHash(b, &b.origHash) + } + } + + b.Path = filename + absPath, _ := filepath.Abs(filename) + b.AbsPath = absPath + b.isModified = false + return b.Serialize() +} + +// SaveWithSudo saves the buffer to the default path with sudo +func (b *Buffer) SaveWithSudo() error { + return b.SaveAsWithSudo(b.Path) +} + +// SaveAsWithSudo is the same as SaveAs except it uses a neat trick +// with tee to use sudo so the user doesn't have to reopen micro with sudo +func (b *Buffer) SaveAsWithSudo(filename string) error { + b.UpdateRules() + b.Path = filename + absPath, _ := filepath.Abs(filename) + b.AbsPath = absPath + + // Set up everything for the command + cmd := exec.Command(config.GlobalSettings["sucmd"].(string), "tee", filename) + cmd.Stdin = bytes.NewBuffer(b.Bytes()) + + // 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() { + for range c { + cmd.Process.Kill() + } + }() + + // Start the command + cmd.Start() + err := cmd.Wait() + + if err == nil { + b.isModified = false + b.ModTime, _ = GetModTime(filename) + return b.Serialize() + } + return err +} diff --git a/cmd/micro/buffer/serialize.go b/cmd/micro/buffer/serialize.go new file mode 100644 index 00000000..d8fc4120 --- /dev/null +++ b/cmd/micro/buffer/serialize.go @@ -0,0 +1,61 @@ +package buffer + +import ( + "encoding/gob" + "errors" + "io" + "os" + + "github.com/zyedidia/micro/cmd/micro/config" + . "github.com/zyedidia/micro/cmd/micro/util" +) + +func init() { + gob.Register(TextEvent{}) + gob.Register(SerializedBuffer{}) +} + +// Serialize serializes the buffer to config.ConfigDir/buffers +func (b *Buffer) Serialize() error { + if !b.Settings["savecursor"].(bool) && !b.Settings["saveundo"].(bool) { + return nil + } + + name := config.ConfigDir + "/buffers/" + EscapePath(b.AbsPath) + + return overwriteFile(name, func(file io.Writer) error { + return gob.NewEncoder(file).Encode(SerializedBuffer{ + b.EventHandler, + b.GetActiveCursor().Loc, + b.ModTime, + }) + }) +} + +func (b *Buffer) Unserialize() error { + // If either savecursor or saveundo is turned on, we need to load the serialized information + // from ~/.config/micro/buffers + file, err := os.Open(config.ConfigDir + "/buffers/" + EscapePath(b.AbsPath)) + defer file.Close() + if err == nil { + var buffer SerializedBuffer + decoder := gob.NewDecoder(file) + gob.Register(TextEvent{}) + err = decoder.Decode(&buffer) + if err != nil { + return errors.New(err.Error() + "\nYou may want to remove the files in ~/.config/micro/buffers (these files store the information for the 'saveundo' and 'savecursor' options) if this problem persists.") + } + if b.Settings["savecursor"].(bool) { + b.StartCursor = buffer.Cursor + } + + if b.Settings["saveundo"].(bool) { + // We should only use last time's eventhandler if the file wasn't modified by someone else in the meantime + if b.ModTime == buffer.ModTime { + b.EventHandler = buffer.EventHandler + b.EventHandler.buf = b + } + } + } + return err +} diff --git a/cmd/micro/micro.go b/cmd/micro/micro.go index dca53a20..070c860d 100644 --- a/cmd/micro/micro.go +++ b/cmd/micro/micro.go @@ -6,6 +6,7 @@ import ( "os" "github.com/go-errors/errors" + "github.com/zyedidia/micro/cmd/micro/action" "github.com/zyedidia/micro/cmd/micro/buffer" "github.com/zyedidia/micro/cmd/micro/config" "github.com/zyedidia/micro/cmd/micro/screen" @@ -105,7 +106,7 @@ func main() { util.TermMessage(err) } config.InitGlobalSettings() - InitBindings() + action.InitBindings() err = config.InitColorscheme() if err != nil { util.TermMessage(err) @@ -126,7 +127,7 @@ func main() { } }() - TryBindKey("Ctrl-z", "Undo", true) + action.TryBindKey("Ctrl-z", "Undo", true) b, err := buffer.NewBufferFromFile(os.Args[1]) @@ -137,7 +138,7 @@ func main() { width, height := screen.Screen.Size() w := NewWindow(0, 0, width, height-1, b) - a := NewBufActionHandler(b, w) + a := action.NewBufHandler(b) // Here is the event loop which runs in a separate thread go func() { diff --git a/cmd/micro/statusline.go b/cmd/micro/statusline.go index 5def796d..0b8f7cab 100644 --- a/cmd/micro/statusline.go +++ b/cmd/micro/statusline.go @@ -8,6 +8,7 @@ import ( "strconv" "unicode/utf8" + "github.com/zyedidia/micro/cmd/micro/action" "github.com/zyedidia/micro/cmd/micro/buffer" "github.com/zyedidia/micro/cmd/micro/config" "github.com/zyedidia/micro/cmd/micro/screen" @@ -84,7 +85,7 @@ func (s *StatusLine) Display() { return []byte(fmt.Sprint(s.FindOpt(string(option)))) } else if bytes.HasPrefix(name, []byte("bind")) { binding := string(name[5:]) - for k, v := range bindings { + for k, v := range action.Bindings { if v == binding { return []byte(k) }