Skip to content

Commit

Permalink
Fix how wide runes are displayed
Browse files Browse the repository at this point in the history
Internal cell buffer represent each rune which should be displayed.
This cell buffer is translated into tcell representation in `draw`.
If wide rune is presented, it will be represented in `tcell` by one cell
with the rune and following with "empty" cells.

Internal representation is fixed in multiple places so it's not mixed
with interpreting rune width.

This fixes also problem with `SetWritePos` function.
  • Loading branch information
dankox committed Nov 27, 2021
1 parent 986ca1c commit 9fa55d6
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 37 deletions.
52 changes: 18 additions & 34 deletions edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ package gocui

import (
"errors"

"github.com/mattn/go-runewidth"
)

// Editor interface must be satisfied by gocui editors.
Expand Down Expand Up @@ -47,13 +45,13 @@ func simpleEditor(v *View, key Key, ch rune, mod Modifier) {
case KeyEnter:
v.EditNewLine()
case KeyArrowDown:
v.moveCursor(0, 1)
v.MoveCursor(0, 1)
case KeyArrowUp:
v.moveCursor(0, -1)
v.MoveCursor(0, -1)
case KeyArrowLeft:
v.moveCursor(-1, 0)
v.MoveCursor(-1, 0)
case KeyArrowRight:
v.moveCursor(1, 0)
v.MoveCursor(1, 0)
case KeyTab:
v.EditWrite('\t')
case KeyEsc:
Expand All @@ -66,7 +64,7 @@ func simpleEditor(v *View, key Key, ch rune, mod Modifier) {
// EditWrite writes a rune at the cursor position.
func (v *View) EditWrite(ch rune) {
v.writeRune(v.cx, v.cy, ch)
v.moveCursor(1, 0)
v.MoveCursor(1, 0)
}

// EditDeleteToStartOfLine is the equivalent of pressing ctrl+U in your terminal, it deletes to the start of the line. Or if you are already at the start of the line, it deletes the newline character
Expand All @@ -87,7 +85,7 @@ func (v *View) EditDeleteToStartOfLine() {
func (v *View) EditGotoToStartOfLine() {
x, _ := v.Cursor()
for x > 0 {
v.moveCursor(-1, 0)
v.MoveCursor(-1, 0)
x, _ = v.Cursor()
}
}
Expand All @@ -102,12 +100,12 @@ func (v *View) EditGotoToEndOfLine() {
prevX := -1
for prevX != x {
prevX = x
v.moveCursor(1, 0)
v.MoveCursor(1, 0)
x, _ = v.Cursor()
}
} else {
// most left so now we're at the end of the original line
v.moveCursor(-1, 0)
v.MoveCursor(-1, 0)
}
}

Expand All @@ -119,7 +117,7 @@ func (v *View) EditDelete(back bool) {
return
}
if y >= len(v.lines) {
v.moveCursor(-1, 0)
v.MoveCursor(-1, 0)
return
}

Expand All @@ -135,15 +133,16 @@ func (v *View) EditDelete(back bool) {
return
}
if back { // middle/end of the line
n, _ := v.deleteRune(v.cx-1, v.cy)
v.moveCursor(-n, 0)
if err := v.deleteRune(v.cx-1, v.cy); err == nil {
v.MoveCursor(-1, 0)
}
return
}
if x == len(v.lines[y]) { // end of the line
_ = v.mergeLines(y)
return
}
_, _ = v.deleteRune(v.cx, v.cy) // start/middle of the line
v.deleteRune(v.cx, v.cy) // start/middle of the line
}

// EditNewLine inserts a new line under the cursor.
Expand All @@ -156,11 +155,6 @@ func (v *View) EditNewLine() {

// MoveCursor mores the cursor relative from it's current possition
func (v *View) MoveCursor(dx, dy int) {
v.moveCursor(dx, dy)
v.gui.userEvents <- userEvent{func(g *Gui) error { return nil }}
}

func (v *View) moveCursor(dx, dy int) {
newX, newY := v.cx+dx, v.cy+dy

if len(v.lines) == 0 {
Expand Down Expand Up @@ -266,26 +260,16 @@ func (v *View) writeRune(x, y int, ch rune) error {

// deleteRune removes a rune from the view's internal buffer, at the
// position corresponding to the point (x, y).
// returns the amount of columns that where removed.
func (v *View) deleteRune(x, y int) (int, error) {
// returns error if invalid point is specified.
func (v *View) deleteRune(x, y int) error {
v.tainted = true

if x < 0 || y < 0 || y >= len(v.lines) || x >= len(v.lines[y]) {
return 0, errors.New("invalid point")
}

var tw int
for i := range v.lines[y] {
w := runewidth.RuneWidth(v.lines[y][i].chr)
tw += w
if tw > x {
v.lines[y] = append(v.lines[y][:i], v.lines[y][i+1:]...)
return w, nil
}

return errors.New("invalid point")
}

return 0, nil
v.lines[y] = append(v.lines[y][:x], v.lines[y][x+1:]...)
return nil
}

// mergeLines merges the lines "y" and "y+1" if possible.
Expand Down
17 changes: 14 additions & 3 deletions view.go
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,11 @@ func (v *View) draw() error {
if err := v.setRune(x, y, char.chr, fgColor, bgColor); err != nil {
return err
}
x += runewidth.RuneWidth(char.chr)
if char.chr == 0 {
x++ // if NULL increase, so `SetWritePos` can be used (NULL translate to SPACE in setRune)
} else {
x += runewidth.RuneWidth(char.chr)
}
}
y++
}
Expand Down Expand Up @@ -846,7 +850,11 @@ func (v *View) SetHighlight(y int, on bool) error {

func lineWidth(line []cell) (n int) {
for i := range line {
n += runewidth.RuneWidth(line[i].chr)
if line[i].chr == 0 {
n++ // if it's NULL character, it's translated to SPACE in setRune
} else {
n += runewidth.RuneWidth(line[i].chr)
}
}

return
Expand All @@ -872,7 +880,10 @@ func (v *View) takeLine(l *[]cell) (visableLine []cell, width int, end bool) {

for i, cell = range *l {
chr := cell.chr
charWidth := runewidth.RuneWidth(chr)
charWidth := 1 // default for NULL character (translated to SPACE in setRune)
if chr != 0 {
charWidth = runewidth.RuneWidth(chr)
}

if width+charWidth > maxX {
break
Expand Down

0 comments on commit 9fa55d6

Please sign in to comment.