diff --git a/cmd/micro/actions.go b/cmd/micro/actions.go index 3c6f48b2..7d726b08 100644 --- a/cmd/micro/actions.go +++ b/cmd/micro/actions.go @@ -111,7 +111,21 @@ func (v *View) CursorLeft(usePlugin bool) bool { v.Cursor.Loc = v.Cursor.CurSelection[0] v.Cursor.ResetSelection() } else { - v.Cursor.Left() + tabstospaces := v.Buf.Settings["tabstospaces"].(bool) + tabmovement := v.Buf.Settings["tabmovement"].(bool) + if tabstospaces && tabmovement { + tabsize := int(v.Buf.Settings["tabsize"].(float64)) + line := v.Buf.Line(v.Cursor.Y) + if v.Cursor.X-tabsize >= 0 && line[v.Cursor.X-tabsize:v.Cursor.X] == Spaces(tabsize) && IsStrWhitespace(line[0:v.Cursor.X-tabsize]) { + for i := 0; i < tabsize; i++ { + v.Cursor.Left() + } + } else { + v.Cursor.Left() + } + } else { + v.Cursor.Left() + } } if usePlugin { @@ -130,7 +144,21 @@ func (v *View) CursorRight(usePlugin bool) bool { v.Cursor.Loc = v.Cursor.CurSelection[1].Move(-1, v.Buf) v.Cursor.ResetSelection() } else { - v.Cursor.Right() + tabstospaces := v.Buf.Settings["tabstospaces"].(bool) + tabmovement := v.Buf.Settings["tabmovement"].(bool) + if tabstospaces && tabmovement { + tabsize := int(v.Buf.Settings["tabsize"].(float64)) + line := v.Buf.Line(v.Cursor.Y) + if v.Cursor.X+tabsize < Count(line) && line[v.Cursor.X:v.Cursor.X+tabsize] == Spaces(tabsize) && IsStrWhitespace(line[0:v.Cursor.X]) { + for i := 0; i < tabsize; i++ { + v.Cursor.Right() + } + } else { + v.Cursor.Right() + } + } else { + v.Cursor.Right() + } } if usePlugin { diff --git a/cmd/micro/settings.go b/cmd/micro/settings.go index 560a1716..c0ed53b3 100644 --- a/cmd/micro/settings.go +++ b/cmd/micro/settings.go @@ -196,6 +196,7 @@ func DefaultGlobalSettings() map[string]interface{} { "splitBottom": true, "statusline": true, "syntax": true, + "tabmovement": false, "tabsize": float64(4), "tabstospaces": false, "termtitle": false, @@ -231,6 +232,7 @@ func DefaultLocalSettings() map[string]interface{} { "splitBottom": true, "statusline": true, "syntax": true, + "tabmovement": false, "tabsize": float64(4), "tabstospaces": false, "useprimary": true, diff --git a/cmd/micro/util.go b/cmd/micro/util.go index 5b2cfac8..25d209d7 100644 --- a/cmd/micro/util.go +++ b/cmd/micro/util.go @@ -82,6 +82,16 @@ func IsWhitespace(c rune) bool { return c == ' ' || c == '\t' || c == '\n' } +// IsStrWhitespace returns true if the given string is all whitespace +func IsStrWhitespace(str string) bool { + for _, c := range str { + if !IsWhitespace(c) { + return false + } + } + return true +} + // Contains returns whether or not a string array contains a given string func Contains(list []string, a string) bool { for _, b := range list { diff --git a/runtime/help/options.md b/runtime/help/options.md index 40ac5eb5..03fb2522 100644 --- a/runtime/help/options.md +++ b/runtime/help/options.md @@ -65,6 +65,11 @@ Here are the options that you can set: default value: `off` +* `tabmovement`: navigate spaces at the beginning of lines as if they are tabs (e.g. move over 4 spaces at once). + This option only does anything if `tabstospaces` is on. + + default value: `off` + * `autoindent`: when creating a new line use the same indentation as the previous line