+// 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
+}
+