import (
"fmt"
"os"
+ "regexp"
"strconv"
"strings"
"time"
+ "unicode/utf8"
"github.com/yuin/gopher-lua"
"github.com/zyedidia/clipboard"
+ "github.com/zyedidia/micro/cmd/micro/shellwords"
"github.com/zyedidia/tcell"
)
v.Cursor.ResetSelection()
v.Relocate()
}
- if time.Since(v.lastClickTime)/time.Millisecond < doubleClickThreshold {
+ if time.Since(v.lastClickTime)/time.Millisecond < doubleClickThreshold && (x == v.lastLoc.X && y == v.lastLoc.Y) {
if v.doubleClick {
// Triple click
v.lastClickTime = time.Now()
}
}
+ v.lastLoc = Loc{x, y}
+
if usePlugin {
PostActionCall("MousePress", v, e)
}
}
if v.Cursor.HasSelection() {
- v.Cursor.Loc = v.Cursor.CurSelection[1].Move(-1, v.Buf)
+ v.Cursor.Loc = v.Cursor.CurSelection[1]
v.Cursor.ResetSelection()
v.Cursor.StoreVisualX()
} else {
}
loc := v.Cursor.Loc
- count := v.Buf.End().Move(-1, v.Buf)
+ count := v.Buf.End()
if loc.GreaterThan(count) {
loc = count
}
}
loc := v.Cursor.Loc
- count := v.Buf.End().Move(-1, v.Buf)
+ count := v.Buf.End()
if loc.GreaterThan(count) {
loc = count
}
return true
}
+// SelectLine selects the entire current line
+func (v *View) SelectLine(usePlugin bool) bool {
+ if usePlugin && !PreActionCall("SelectLine", v) {
+ return false
+ }
+
+ v.Cursor.SelectLine()
+
+ if usePlugin {
+ return PostActionCall("SelectLine", v)
+ }
+ return true
+}
+
// SelectToStartOfLine selects to the start of the current line
func (v *View) SelectToStartOfLine(usePlugin bool) bool {
if usePlugin && !PreActionCall("SelectToStartOfLine", v) {
return true
}
+// ParagraphPrevious moves the cursor to the previous empty line, or beginning of the buffer if there's none
+func (v *View) ParagraphPrevious(usePlugin bool) bool {
+ if usePlugin && !PreActionCall("ParagraphPrevious", v) {
+ return false
+ }
+ var line int
+ for line = v.Cursor.Y; line > 0; line-- {
+ if len(v.Buf.lines[line].data) == 0 && line != v.Cursor.Y {
+ v.Cursor.X = 0
+ v.Cursor.Y = line
+ break
+ }
+ }
+ // If no empty line found. move cursor to end of buffer
+ if line == 0 {
+ v.Cursor.Loc = v.Buf.Start()
+ }
+
+ if usePlugin {
+ return PostActionCall("ParagraphPrevious", v)
+ }
+ return true
+}
+
+// ParagraphNext moves the cursor to the next empty line, or end of the buffer if there's none
+func (v *View) ParagraphNext(usePlugin bool) bool {
+ if usePlugin && !PreActionCall("ParagraphNext", v) {
+ return false
+ }
+
+ var line int
+ for line = v.Cursor.Y; line < len(v.Buf.lines); line++ {
+ if len(v.Buf.lines[line].data) == 0 && line != v.Cursor.Y {
+ v.Cursor.X = 0
+ v.Cursor.Y = line
+ break
+ }
+ }
+ // If no empty line found. move cursor to end of buffer
+ if line == len(v.Buf.lines) {
+ v.Cursor.Loc = v.Buf.End()
+ }
+
+ if usePlugin {
+ return PostActionCall("ParagraphNext", v)
+ }
+ return true
+}
+
+// Retab changes all tabs to spaces or all spaces to tabs depending
+// on the user's settings
+func (v *View) Retab(usePlugin bool) bool {
+ if usePlugin && !PreActionCall("Retab", v) {
+ return false
+ }
+
+ toSpaces := v.Buf.Settings["tabstospaces"].(bool)
+ tabsize := int(v.Buf.Settings["tabsize"].(float64))
+ dirty := false
+
+ for i := 0; i < v.Buf.NumLines; i++ {
+ l := v.Buf.Line(i)
+
+ ws := GetLeadingWhitespace(l)
+ if ws != "" {
+ if toSpaces {
+ ws = strings.Replace(ws, "\t", Spaces(tabsize), -1)
+ } else {
+ ws = strings.Replace(ws, Spaces(tabsize), "\t", -1)
+ }
+ }
+
+ l = strings.TrimLeft(l, " \t")
+ v.Buf.lines[i].data = []byte(ws + l)
+ dirty = true
+ }
+
+ v.Buf.IsModified = dirty
+
+ if usePlugin {
+ return PostActionCall("Retab", v)
+ }
+ return true
+}
+
// CursorStart moves the cursor to the start of the buffer
func (v *View) CursorStart(usePlugin bool) bool {
if usePlugin && !PreActionCall("CursorStart", v) {
}
ws := GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))
+ cx := v.Cursor.X
v.Buf.Insert(v.Cursor.Loc, "\n")
// v.Cursor.Right()
if v.Buf.Settings["autoindent"].(bool) {
+ if cx < len(ws) {
+ ws = ws[0:cx]
+ }
v.Buf.Insert(v.Cursor.Loc, ws)
// for i := 0; i < len(ws); i++ {
// v.Cursor.Right()
// If the user is using spaces instead of tabs and they are deleting
// whitespace at the start of the line, we should delete as if it's a
// tab (tabSize number of spaces)
- lineStart := v.Buf.Line(v.Cursor.Y)[:v.Cursor.X]
+ lineStart := sliceEnd(v.Buf.LineBytes(v.Cursor.Y), v.Cursor.X)
tabSize := int(v.Buf.Settings["tabsize"].(float64))
- if v.Buf.Settings["tabstospaces"].(bool) && IsSpaces(lineStart) && len(lineStart) != 0 && len(lineStart)%tabSize == 0 {
+ if v.Buf.Settings["tabstospaces"].(bool) && IsSpaces(lineStart) && utf8.RuneCount(lineStart) != 0 && utf8.RuneCount(lineStart)%tabSize == 0 {
loc := v.Cursor.Loc
v.Buf.Remove(loc.Move(-tabSize, v.Buf), loc)
} else {
end := v.Cursor.CurSelection[1]
if end.Y < start.Y {
start, end = end, start
+ v.Cursor.SetSelectionStart(start)
+ v.Cursor.SetSelectionEnd(end)
}
startY := start.Y
endY := end.Move(-1, v.Buf).Y
endX := end.Move(-1, v.Buf).X
+ tabsize := len(v.Buf.IndentString())
for y := startY; y <= endY; y++ {
- tabsize := len(v.Buf.IndentString())
v.Buf.Insert(Loc{0, y}, v.Buf.IndentString())
if y == startY && start.X > 0 {
v.Cursor.SetSelectionStart(start.Move(tabsize, v.Buf))
end := v.Cursor.CurSelection[1]
if end.Y < start.Y {
start, end = end, start
+ v.Cursor.SetSelectionStart(start)
+ v.Cursor.SetSelectionEnd(end)
}
startY := start.Y
return false
}
- if v.Type.scratch == true {
+ if v.Type.Scratch == true {
// We can't save any view type with scratch set. eg help and log text
return false
}
filename, canceled := messenger.Prompt("Filename: ", "", "Save", NoCompletion)
if !canceled {
// the filename might or might not be quoted, so unquote first then join the strings.
- filename = strings.Join(SplitCommandArgs(filename), " ")
+ args, err := shellwords.Split(filename)
+ filename = strings.Join(args, " ")
+ if err != nil {
+ messenger.Error("Error parsing arguments: ", err)
+ return false
+ }
v.saveToFile(filename)
}
return false
}
+ if v.Buf.curCursor == 0 {
+ v.Buf.clearCursors()
+ }
+
v.Buf.Undo()
messenger.Message("Undid action")
return false
}
+ if v.Buf.curCursor == 0 {
+ v.Buf.clearCursors()
+ }
+
v.Buf.Redo()
messenger.Message("Redid action")
return true
}
+// JumpToMatchingBrace moves the cursor to the matching brace if it is
+// currently on a brace
+func (v *View) JumpToMatchingBrace(usePlugin bool) bool {
+ if usePlugin && !PreActionCall("JumpToMatchingBrace", v) {
+ return false
+ }
+
+ for _, bp := range bracePairs {
+ r := v.Cursor.RuneUnder(v.Cursor.X)
+ if r == bp[0] || r == bp[1] {
+ matchingBrace := v.Buf.FindMatchingBrace(bp, v.Cursor.Loc)
+ v.Cursor.GotoLoc(matchingBrace)
+ }
+ }
+
+ if usePlugin {
+ return PostActionCall("JumpToMatchingBrace", v)
+ }
+ return true
+}
+
// SelectAll selects the entire buffer
func (v *View) SelectAll(usePlugin bool) bool {
if usePlugin && !PreActionCall("SelectAll", v) {
}
// Prompt for line number
- message := fmt.Sprintf("Jump to line (1 - %v) # ", v.Buf.NumLines)
- linestring, canceled := messenger.Prompt(message, "", "LineNumber", NoCompletion)
+ message := fmt.Sprintf("Jump to line:col (1 - %v) # ", v.Buf.NumLines)
+ input, canceled := messenger.Prompt(message, "", "LineNumber", NoCompletion)
if canceled {
return false
}
- lineint, err := strconv.Atoi(linestring)
- lineint = lineint - 1 // fix offset
- if err != nil {
- messenger.Error(err) // return errors
- return false
+ var lineInt int
+ var colInt int
+ var err error
+ if strings.Contains(input, ":") {
+ split := strings.Split(input, ":")
+ lineInt, err = strconv.Atoi(split[0])
+ if err != nil {
+ messenger.Message("Invalid line number")
+ return false
+ }
+ colInt, err = strconv.Atoi(split[1])
+ if err != nil {
+ messenger.Message("Invalid column number")
+ return false
+ }
+ } else {
+ lineInt, err = strconv.Atoi(input)
+ if err != nil {
+ messenger.Message("Invalid line number")
+ return false
+ }
}
+ lineInt--
// Move cursor and view if possible.
- if lineint < v.Buf.NumLines && lineint >= 0 {
- v.Cursor.X = 0
- v.Cursor.Y = lineint
+ if lineInt < v.Buf.NumLines && lineInt >= 0 {
+ v.Cursor.X = colInt
+ v.Cursor.Y = lineInt
if usePlugin {
return PostActionCall("JumpLine", v)
return true
}
+// ToggleKeyMenu toggles the keymenu option and resizes all tabs
+func (v *View) ToggleKeyMenu(usePlugin bool) bool {
+ if v.mainCursor() {
+ if usePlugin && !PreActionCall("ToggleBindings", v) {
+ return false
+ }
+
+ globalSettings["keymenu"] = !globalSettings["keymenu"].(bool)
+ for _, tab := range tabs {
+ tab.Resize()
+ }
+
+ if usePlugin {
+ return PostActionCall("ToggleBindings", v)
+ }
+ }
+ return true
+}
+
// ShellMode opens a terminal to run a shell command
func (v *View) ShellMode(usePlugin bool) bool {
if v.mainCursor() {
return false
}
+// ToggleOverwriteMode lets the user toggle the text overwrite mode
+func (v *View) ToggleOverwriteMode(usePlugin bool) bool {
+ if v.mainCursor() {
+ if usePlugin && !PreActionCall("ToggleOverwriteMode", v) {
+ return false
+ }
+
+ v.isOverwriteMode = !v.isOverwriteMode
+
+ if usePlugin {
+ return PostActionCall("ToggleOverwriteMode", v)
+ }
+ }
+ return false
+}
+
// Escape leaves current mode
func (v *View) Escape(usePlugin bool) bool {
if v.mainCursor() {
}
screen.Fini()
+ messenger.SaveHistory()
os.Exit(0)
}
}
}
screen.Fini()
+ messenger.SaveHistory()
os.Exit(0)
}
}
searchStart = spawner.CurSelection[1]
v.Cursor = c
- Search(sel, v, true)
+ Search(regexp.QuoteMeta(sel), v, true)
for _, cur := range v.Buf.cursors {
if c.Loc == cur.Loc {
searchStart = cursor.CurSelection[1]
v.Cursor = cursor
- Search(sel, v, true)
+ Search(regexp.QuoteMeta(sel), v, true)
v.Relocate()
v.Cursor = cursor
return false
}
- for i := 1; i < len(v.Buf.cursors); i++ {
- v.Buf.cursors[i] = nil
- }
- v.Buf.cursors = v.Buf.cursors[:1]
- v.Buf.UpdateCursors()
- v.Cursor.ResetSelection()
+ v.Buf.clearCursors()
v.Relocate()
if usePlugin {