From d6307b2718d6f3a1e40ffd0fce5f2e564cdd4e49 Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Sat, 4 Jun 2016 16:25:11 -0400 Subject: [PATCH] Proper support for double width characters Fixes #99 --- cmd/micro/cursor.go | 19 +++++++------------ cmd/micro/util.go | 24 ++++++++++++++++++++++++ cmd/micro/view.go | 11 +++++++++++ 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/cmd/micro/cursor.go b/cmd/micro/cursor.go index a2ca8b44..19f36dfc 100644 --- a/cmd/micro/cursor.go +++ b/cmd/micro/cursor.go @@ -1,9 +1,5 @@ package main -import ( - "strings" -) - // FromCharPos converts from a character position to an x, y position func FromCharPos(loc int, buf *Buffer) (int, int) { return FromCharPosStart(0, 0, 0, loc, buf) @@ -357,13 +353,13 @@ func (c *Cursor) GetCharPosInLine(lineNum, visualPos int) int { // Get the tab size tabSize := int(settings["tabsize"].(float64)) // This is the visual line -- every \t replaced with the correct number of spaces - visualLine := strings.Replace(c.buf.Lines[lineNum], "\t", "\t"+Spaces(tabSize-1), -1) - if visualPos > Count(visualLine) { - visualPos = Count(visualLine) + visualLineLen := StringWidth(c.buf.Lines[lineNum]) + if visualPos > visualLineLen { + visualPos = visualLineLen } - numTabs := NumOccurences(visualLine[:visualPos], '\t') - if visualPos >= (tabSize-1)*numTabs { - return visualPos - (tabSize-1)*numTabs + width := WidthOfLargeRunes(c.buf.Lines[lineNum]) + if visualPos >= width { + return visualPos - width } return visualPos / tabSize } @@ -371,8 +367,7 @@ func (c *Cursor) GetCharPosInLine(lineNum, visualPos int) int { // GetVisualX returns the x value of the cursor in visual spaces func (c *Cursor) GetVisualX() int { runes := []rune(c.buf.Lines[c.Y]) - tabSize := int(settings["tabsize"].(float64)) - return c.X + NumOccurences(string(runes[:c.X]), '\t')*(tabSize-1) + return StringWidth(string(runes[:c.X])) } // Relocate makes sure that the cursor is inside the bounds of the buffer diff --git a/cmd/micro/util.go b/cmd/micro/util.go index 02dacf38..46da66da 100644 --- a/cmd/micro/util.go +++ b/cmd/micro/util.go @@ -7,6 +7,8 @@ import ( "strings" "time" "unicode/utf8" + + "github.com/mattn/go-runewidth" ) // Util.go is a collection of utility functions that are used throughout @@ -138,6 +140,28 @@ func GetModTime(path string) (time.Time, bool) { return info.ModTime(), true } +func StringWidth(str string) int { + sw := runewidth.StringWidth(str) + sw += NumOccurences(str, '\t') * (int(settings["tabsize"].(float64)) - 1) + return sw +} + +func WidthOfLargeRunes(str string) int { + count := 0 + for _, ch := range str { + var w int + if ch == '\t' { + w = int(settings["tabsize"].(float64)) + } else { + w = runewidth.RuneWidth(ch) + } + if w > 1 { + count += (w - 1) + } + } + return count +} + func runePos(p int, str string) int { return utf8.RuneCountInString(str[:p]) } diff --git a/cmd/micro/view.go b/cmd/micro/view.go index cd2fc324..0940acc9 100644 --- a/cmd/micro/view.go +++ b/cmd/micro/view.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/mattn/go-runewidth" "github.com/zyedidia/tcell" ) @@ -601,6 +602,16 @@ func (v *View) DisplayView() { screen.SetContent(x-v.leftCol, lineN, ' ', nil, lineStyle) } } + } else if runewidth.RuneWidth(ch) > 1 { + if x-v.leftCol >= v.lineNumOffset { + screen.SetContent(x-v.leftCol, lineN, ch, nil, lineStyle) + } + for i := 0; i < runewidth.RuneWidth(ch)-1; i++ { + x++ + if x-v.leftCol >= v.lineNumOffset { + screen.SetContent(x-v.leftCol, lineN, ' ', nil, lineStyle) + } + } } else { if x-v.leftCol >= v.lineNumOffset { screen.SetContent(x-v.leftCol, lineN, ch, nil, lineStyle)