From 107468b6a2daf29dab9b74eb9e2f7218d74412be Mon Sep 17 00:00:00 2001 From: Uchino Jun Date: Thu, 10 Jan 2019 20:02:03 +0900 Subject: [PATCH 01/18] Modify the deprecated func --- up.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/up.go b/up.go index be6a877..09dbd31 100644 --- a/up.go +++ b/up.go @@ -296,15 +296,15 @@ func (e *Editor) String() string { return string(e.value) } func (e *Editor) DrawTo(region Region, style tcell.Style, setcursor func(x, y int)) { // Draw prompt & the edited value - use white letters on blue background for i, ch := range e.prompt { - region.SetCell(i, 0, style, ch) + region.SetContent(i, 0, ch, nil, style) } for i, ch := range e.value { - region.SetCell(len(e.prompt)+i, 0, style, ch) + region.SetContent(len(e.prompt)+i, 0, ch, nil, style) } // Clear remains of last value if needed for i := len(e.value); i < e.lastw; i++ { - region.SetCell(len(e.prompt)+i, 0, tcell.StyleDefault, ' ') + region.SetContent(len(e.prompt)+i, 0, ' ', nil, tcell.StyleDefault) } e.lastw = len(e.value) @@ -419,7 +419,7 @@ func (v *BufView) DrawTo(region Region) { if x >= region.W { x, ch = region.W-1, '»' } - region.SetCell(x, y, tcell.StyleDefault, ch) + region.SetContent(x, y, ch, nil, tcell.StyleDefault) } endline := func(x, y int) { x -= v.X @@ -431,7 +431,7 @@ func (v *BufView) DrawTo(region Region) { } lclip = false for ; x < region.W; x++ { - region.SetCell(x, y, tcell.StyleDefault, ' ') + region.SetContent(x, y, ' ', nil, tcell.StyleDefault) } } @@ -625,7 +625,7 @@ func (b *Buf) DrawStatus(region Region, style tcell.Style) { } b.mu.Unlock() - region.SetCell(0, 0, style, status) + region.SetContent(0, 0, status, nil, style) } func (b *Buf) NewReader(blocking bool) io.Reader { @@ -775,15 +775,15 @@ fallback_print: type Region struct { W, H int - SetCell func(x, y int, style tcell.Style, ch rune) + SetContent func(x, y int, mainc rune, combc []rune, style tcell.Style) } func TuiRegion(tui tcell.Screen, x, y, w, h int) Region { return Region{ W: w, H: h, - SetCell: func(dx, dy int, style tcell.Style, ch rune) { + SetContent: func(dx, dy int, mainc rune, combc []rune, style tcell.Style) { if dx >= 0 && dx < w && dy >= 0 && dy < h { - tui.SetCell(x+dx, y+dy, style, ch) + tui.SetContent(x+dx, y+dy, mainc, combc, style) } }, } @@ -796,6 +796,6 @@ var ( func drawText(region Region, style tcell.Style, text string) { for x, ch := range text { - region.SetCell(x, 0, style, ch) + region.SetContent(x, 0, ch, nil, style) } } From 7eb833492499989e1cc86d401d0d43e8f4d5f3be Mon Sep 17 00:00:00 2001 From: Uchino Jun Date: Mon, 8 Apr 2019 23:14:02 +0900 Subject: [PATCH 02/18] Modified drawText function to support combining characters --- up.go | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/up.go b/up.go index 09dbd31..19c5d8d 100644 --- a/up.go +++ b/up.go @@ -29,10 +29,12 @@ import ( "os" "os/exec" "sync" + "unicode" "github.com/gdamore/tcell" "github.com/gdamore/tcell/terminfo" "github.com/mattn/go-isatty" + "github.com/mattn/go-runewidth" "github.com/spf13/pflag" ) @@ -795,7 +797,23 @@ var ( ) func drawText(region Region, style tcell.Style, text string) { - for x, ch := range text { - region.SetContent(x, 0, ch, nil, style) + // the primary non-zero width rune + var mainc rune + // the array that follows is a possible list of combining characters to append + combc := make([]rune, 0) + var pos int + + for _, ch := range text { + if unicode.IsMark(ch) { + combc = append(combc, ch) + } else { + region.SetContent(pos, 0, mainc, combc, style) + pos += runewidth.RuneWidth(mainc) + mainc, combc = ch, nil + } + } + // print the last character + if len(text) != 0 { + region.SetContent(pos, 0, mainc, combc, style) } } From 950747ffb70206023ecf87c890054b4917285416 Mon Sep 17 00:00:00 2001 From: Uchino Jun Date: Mon, 8 Apr 2019 23:19:08 +0900 Subject: [PATCH 03/18] Modified drawText function to support the offset of text --- up.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/up.go b/up.go index 19c5d8d..07c41a1 100644 --- a/up.go +++ b/up.go @@ -174,7 +174,7 @@ func main() { commandEditor.DrawTo(TuiRegion(tui, 1, 0, w-1, 1), style, func(x, y int) { tui.ShowCursor(x+1, 0) }) commandOutput.DrawTo(TuiRegion(tui, 0, 1, w, h-1)) - drawText(TuiRegion(tui, 0, h-1, w, 1), whiteOnBlue, message) + drawText(TuiRegion(tui, 0, h-1, w, 1), 0, whiteOnBlue, message) tui.Show() // Handle UI events @@ -796,24 +796,23 @@ var ( whiteOnDBlue = tcell.StyleDefault.Foreground(tcell.ColorWhite).Background(tcell.ColorNavy) ) -func drawText(region Region, style tcell.Style, text string) { +func drawText(region Region, x int, style tcell.Style, text string) { // the primary non-zero width rune var mainc rune // the array that follows is a possible list of combining characters to append combc := make([]rune, 0) - var pos int for _, ch := range text { if unicode.IsMark(ch) { combc = append(combc, ch) } else { - region.SetContent(pos, 0, mainc, combc, style) - pos += runewidth.RuneWidth(mainc) + region.SetContent(x, 0, mainc, combc, style) + x += runewidth.RuneWidth(mainc) mainc, combc = ch, nil } } // print the last character if len(text) != 0 { - region.SetContent(pos, 0, mainc, combc, style) + region.SetContent(x, 0, mainc, combc, style) } } From 68133420f8456d13a292c2ba79affc1357b61456 Mon Sep 17 00:00:00 2001 From: Uchino Jun Date: Mon, 8 Apr 2019 23:24:54 +0900 Subject: [PATCH 04/18] Modified (*Editor)DrawTo method to support combining characters --- up.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/up.go b/up.go index 07c41a1..294ad4c 100644 --- a/up.go +++ b/up.go @@ -297,22 +297,18 @@ func (e *Editor) String() string { return string(e.value) } func (e *Editor) DrawTo(region Region, style tcell.Style, setcursor func(x, y int)) { // Draw prompt & the edited value - use white letters on blue background - for i, ch := range e.prompt { - region.SetContent(i, 0, ch, nil, style) - } - for i, ch := range e.value { - region.SetContent(len(e.prompt)+i, 0, ch, nil, style) - } + drawText(region, 0, style, string(e.prompt)) + drawText(region, runewidth.StringWidth(string(e.prompt)), style, string(e.value)) // Clear remains of last value if needed - for i := len(e.value); i < e.lastw; i++ { - region.SetContent(len(e.prompt)+i, 0, ' ', nil, tcell.StyleDefault) + for i := runewidth.StringWidth(string(e.value)); i < e.lastw; i++ { + region.SetContent(runewidth.StringWidth(string(e.prompt))+i, 0, ' ', nil, tcell.StyleDefault) } - e.lastw = len(e.value) + e.lastw = runewidth.StringWidth(string(e.value)) // Show cursor if requested if setcursor != nil { - setcursor(len(e.prompt)+e.cursor, 0) + setcursor(runewidth.StringWidth(string(e.prompt))+runewidth.StringWidth(string(e.value[:e.cursor])), 0) } } From a55e206fae32ae35922fabded970fd7256c0444d Mon Sep 17 00:00:00 2001 From: Uchino Jun Date: Mon, 8 Apr 2019 23:27:21 +0900 Subject: [PATCH 05/18] Enable moving the cursor on Editor properly for strings that contain combining characters --- up.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/up.go b/up.go index 294ad4c..b195486 100644 --- a/up.go +++ b/up.go @@ -328,14 +328,22 @@ func (e *Editor) HandleKey(ev *tcell.EventKey) bool { case key(tcell.KeyLeft), key(tcell.KeyCtrlB), ctrlKey(tcell.KeyCtrlB): + // move left until the previous primary rune if e.cursor > 0 { e.cursor-- + for unicode.IsMark(rune(e.value[e.cursor])) { + e.cursor-- + } } case key(tcell.KeyRight), key(tcell.KeyCtrlF), ctrlKey(tcell.KeyCtrlF): + // move right until the next primary rune or the end of line if e.cursor < len(e.value) { e.cursor++ + for e.cursor < len(e.value) && unicode.IsMark(rune(e.value[e.cursor])) { + e.cursor++ + } } case key(tcell.KeyCtrlA), ctrlKey(tcell.KeyCtrlA): From df224fa7cf4fdb34a6c952e566e0f407022ad2b1 Mon Sep 17 00:00:00 2001 From: Uchino Jun Date: Mon, 8 Apr 2019 23:29:37 +0900 Subject: [PATCH 06/18] Count the actual delta of e.cursor, taking combining characters into account --- up.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/up.go b/up.go index b195486..cf7864b 100644 --- a/up.go +++ b/up.go @@ -373,11 +373,25 @@ func (e *Editor) insert(ch ...rune) { } func (e *Editor) delete(dx int) { - pos := e.cursor + dx + if e.cursor == 0 { + return + } + + // count the actual delta of e.cursor, taking combining characters into account + rune_dx := 0 + for ; dx < 0; rune_dx-- { + if unicode.IsMark(rune(e.value[e.cursor+rune_dx-1])) { + continue + } else { + dx++ + } + } + + pos := e.cursor + rune_dx if pos < 0 || pos >= len(e.value) { return } - e.value = append(e.value[:pos], e.value[pos+1:]...) + e.value = append(e.value[:pos], e.value[pos-rune_dx:]...) e.cursor = pos } From 8c56ee2b464f02a99060d0c0ed583fb14193d720 Mon Sep 17 00:00:00 2001 From: Uchino Jun Date: Mon, 8 Apr 2019 23:33:29 +0900 Subject: [PATCH 07/18] Modified drawch to support combining characters --- up.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/up.go b/up.go index cf7864b..85b867b 100644 --- a/up.go +++ b/up.go @@ -429,17 +429,17 @@ func (v *BufView) DrawTo(region Region) { } lclip := false - drawch := func(x, y int, ch rune) { + drawch := func(x, y int, mainc rune, combc []rune) { if x <= v.X && v.X != 0 { - x, ch = 0, '«' + x, mainc = 0, '«' lclip = true } else { x -= v.X } if x >= region.W { - x, ch = region.W-1, '»' + x, mainc = region.W-1, '»' } - region.SetContent(x, y, ch, nil, tcell.StyleDefault) + region.SetContent(x, y, mainc, combc, tcell.StyleDefault) } endline := func(x, y int) { x -= v.X @@ -471,16 +471,16 @@ func (v *BufView) DrawTo(region Region) { continue case '\t': const tabwidth = 8 - drawch(x, y, ' ') + drawch(x, y, ' ', nil) for x%tabwidth < (tabwidth - 1) { x++ if x >= region.W { break } - drawch(x, y, ' ') + drawch(x, y, ' ', nil) } default: - drawch(x, y, ch) + drawch(x, y, ch, nil) } x++ } From 1ee1b22c8c16fd4c5b685eb99f398c6dddf2a4ef Mon Sep 17 00:00:00 2001 From: Uchino Jun Date: Mon, 8 Apr 2019 23:55:19 +0900 Subject: [PATCH 08/18] Modified (*BufView)DrawTo method to support combining characters --- up.go | 63 +++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/up.go b/up.go index 85b867b..1887018 100644 --- a/up.go +++ b/up.go @@ -456,7 +456,11 @@ func (v *BufView) DrawTo(region Region) { } x, y := 0, 0 - // TODO: handle runes properly, including their visual width (mattn/go-runewidth) + // the primary non-zero width rune + var mainc rune + // the array that follows is a possible list of combining characters to append + combc := make([]rune, 0) + for { ch, _, err := r.ReadRune() if y >= region.H || err == io.EOF { @@ -464,25 +468,50 @@ func (v *BufView) DrawTo(region Region) { } else if err != nil { panic(err) } - switch ch { - case '\n': - endline(x, y) - x, y = 0, y+1 - continue - case '\t': - const tabwidth = 8 - drawch(x, y, ' ', nil) - for x%tabwidth < (tabwidth - 1) { - x++ - if x >= region.W { - break - } + + if unicode.IsMark(ch) { + combc = append(combc, ch) + } else { + switch mainc { + case '\t': + const tabwidth = 8 drawch(x, y, ' ', nil) + for x%tabwidth < (tabwidth - 1) { + x++ + if x >= region.W { + break + } + drawch(x, y, ' ', nil) + } + default: + drawch(x, y, mainc, combc) + x += runewidth.RuneWidth(mainc) + if ch == '\n' { + endline(x, y) + x, y = 0, y+1 + } } - default: - drawch(x, y, ch, nil) + mainc, combc = ch, nil + } + } + + // print the last character + switch mainc { + case '\n': + endline(x, y) + x, y = 0, y+1 + case '\t': + const tabwidth = 8 + drawch(x, y, ' ', nil) + for x%tabwidth < (tabwidth - 1) { + x++ + if x >= region.W { + break + } + drawch(x, y, ' ', nil) } - x++ + default: + drawch(x, y, mainc, combc) } for ; y < region.H; y++ { endline(x, y) From 7ed4fa51d981662d43929b1dfae82029f4665e9f Mon Sep 17 00:00:00 2001 From: Uchino Jun Date: Fri, 26 Apr 2019 09:46:42 +0900 Subject: [PATCH 09/18] Added `combc = nil` --- up.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/up.go b/up.go index 1887018..40356e5 100644 --- a/up.go +++ b/up.go @@ -431,13 +431,13 @@ func (v *BufView) DrawTo(region Region) { lclip := false drawch := func(x, y int, mainc rune, combc []rune) { if x <= v.X && v.X != 0 { - x, mainc = 0, '«' + x, mainc, combc = 0, '«', nil lclip = true } else { x -= v.X } if x >= region.W { - x, mainc = region.W-1, '»' + x, mainc, combc = region.W-1, '»', nil } region.SetContent(x, y, mainc, combc, tcell.StyleDefault) } From b2e6c712307ea0e522be76e98003081c5dbe4a59 Mon Sep 17 00:00:00 2001 From: Uchino Jun Date: Fri, 26 Apr 2019 09:50:54 +0900 Subject: [PATCH 10/18] Modified the type of the last argument of drawText function --- up.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/up.go b/up.go index 40356e5..922658f 100644 --- a/up.go +++ b/up.go @@ -174,7 +174,7 @@ func main() { commandEditor.DrawTo(TuiRegion(tui, 1, 0, w-1, 1), style, func(x, y int) { tui.ShowCursor(x+1, 0) }) commandOutput.DrawTo(TuiRegion(tui, 0, 1, w, h-1)) - drawText(TuiRegion(tui, 0, h-1, w, 1), 0, whiteOnBlue, message) + drawText(TuiRegion(tui, 0, h-1, w, 1), 0, whiteOnBlue, []rune(message)) tui.Show() // Handle UI events @@ -297,8 +297,8 @@ func (e *Editor) String() string { return string(e.value) } func (e *Editor) DrawTo(region Region, style tcell.Style, setcursor func(x, y int)) { // Draw prompt & the edited value - use white letters on blue background - drawText(region, 0, style, string(e.prompt)) - drawText(region, runewidth.StringWidth(string(e.prompt)), style, string(e.value)) + drawText(region, 0, style, []rune(e.prompt)) + drawText(region, runewidth.StringWidth(string(e.prompt)), style, []rune(e.value)) // Clear remains of last value if needed for i := runewidth.StringWidth(string(e.value)); i < e.lastw; i++ { @@ -843,7 +843,7 @@ var ( whiteOnDBlue = tcell.StyleDefault.Foreground(tcell.ColorWhite).Background(tcell.ColorNavy) ) -func drawText(region Region, x int, style tcell.Style, text string) { +func drawText(region Region, x int, style tcell.Style, text []rune) { // the primary non-zero width rune var mainc rune // the array that follows is a possible list of combining characters to append From 093d76256e72732d2e4abeb4e3f039fd30a65f71 Mon Sep 17 00:00:00 2001 From: Uchino Jun Date: Fri, 26 Apr 2019 09:59:22 +0900 Subject: [PATCH 11/18] Deleted the unnecessary cast --- up.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/up.go b/up.go index 922658f..329285b 100644 --- a/up.go +++ b/up.go @@ -341,7 +341,7 @@ func (e *Editor) HandleKey(ev *tcell.EventKey) bool { // move right until the next primary rune or the end of line if e.cursor < len(e.value) { e.cursor++ - for e.cursor < len(e.value) && unicode.IsMark(rune(e.value[e.cursor])) { + for e.cursor < len(e.value) && unicode.IsMark(e.value[e.cursor]) { e.cursor++ } } From 243eb17938d83342342db4161cb759a1e8f2e9ba Mon Sep 17 00:00:00 2001 From: Uchino Jun Date: Fri, 26 Apr 2019 14:07:35 +0900 Subject: [PATCH 12/18] Modified drawText function to return the rune width of the text and modified the type of the last argument for drawText function to []rune --- up.go | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/up.go b/up.go index 329285b..fa31e09 100644 --- a/up.go +++ b/up.go @@ -297,18 +297,18 @@ func (e *Editor) String() string { return string(e.value) } func (e *Editor) DrawTo(region Region, style tcell.Style, setcursor func(x, y int)) { // Draw prompt & the edited value - use white letters on blue background - drawText(region, 0, style, []rune(e.prompt)) - drawText(region, runewidth.StringWidth(string(e.prompt)), style, []rune(e.value)) + wprompt := drawText(region, 0, style, e.prompt) + wvalue := drawText(region, wprompt, style, e.value) // Clear remains of last value if needed - for i := runewidth.StringWidth(string(e.value)); i < e.lastw; i++ { - region.SetContent(runewidth.StringWidth(string(e.prompt))+i, 0, ' ', nil, tcell.StyleDefault) + for i := wvalue; i < e.lastw; i++ { + region.SetContent(wprompt+i, 0, ' ', nil, tcell.StyleDefault) } - e.lastw = runewidth.StringWidth(string(e.value)) + e.lastw = wvalue // Show cursor if requested if setcursor != nil { - setcursor(runewidth.StringWidth(string(e.prompt))+runewidth.StringWidth(string(e.value[:e.cursor])), 0) + setcursor(wprompt+runewidth.StringWidth(string(e.value[:e.cursor])), 0) } } @@ -843,23 +843,23 @@ var ( whiteOnDBlue = tcell.StyleDefault.Foreground(tcell.ColorWhite).Background(tcell.ColorNavy) ) -func drawText(region Region, x int, style tcell.Style, text []rune) { - // the primary non-zero width rune - var mainc rune - // the array that follows is a possible list of combining characters to append - combc := make([]rune, 0) - - for _, ch := range text { - if unicode.IsMark(ch) { - combc = append(combc, ch) - } else { - region.SetContent(x, 0, mainc, combc, style) - x += runewidth.RuneWidth(mainc) - mainc, combc = ch, nil +func drawText(region Region, x int, style tcell.Style, text []rune) int { + w := 0 + // a combining character sequence, see: http://unicode.org/faq/char_combmark.html + seq := []rune{} + drawSeq := func() { + if len(seq) > 0 { + region.SetContent(x+w, 0, seq[0], seq[1:], style) + w += runewidth.RuneWidth(seq[0]) } } - // print the last character - if len(text) != 0 { - region.SetContent(x, 0, mainc, combc, style) + for _, ch := range text { + if !unicode.IsMark(ch) { + drawSeq() + seq = seq[:0] + } + seq = append(seq, ch) } + drawSeq() + return w } From d674d5fa46fa01b5eacdfc2b223b0460b92eff5a Mon Sep 17 00:00:00 2001 From: Uchino Jun Date: Fri, 26 Apr 2019 14:41:54 +0900 Subject: [PATCH 13/18] Added a safety check and removed the unnecessary cast --- up.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/up.go b/up.go index fa31e09..abe776d 100644 --- a/up.go +++ b/up.go @@ -331,7 +331,7 @@ func (e *Editor) HandleKey(ev *tcell.EventKey) bool { // move left until the previous primary rune if e.cursor > 0 { e.cursor-- - for unicode.IsMark(rune(e.value[e.cursor])) { + for e.cursor > 0 && unicode.IsMark(e.value[e.cursor]) { e.cursor-- } } From 56003388a18521592b2e6cfd4ea6d90323f97b89 Mon Sep 17 00:00:00 2001 From: Uchino Jun Date: Sun, 28 Apr 2019 09:09:45 +0900 Subject: [PATCH 14/18] Modified delete method to a simple way --- up.go | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/up.go b/up.go index abe776d..1d34749 100644 --- a/up.go +++ b/up.go @@ -373,25 +373,20 @@ func (e *Editor) insert(ch ...rune) { } func (e *Editor) delete(dx int) { - if e.cursor == 0 { + pos := e.cursor + dx + if pos < 0 || pos >= len(e.value) { return } - - // count the actual delta of e.cursor, taking combining characters into account - rune_dx := 0 - for ; dx < 0; rune_dx-- { - if unicode.IsMark(rune(e.value[e.cursor+rune_dx-1])) { - continue - } else { - dx++ - } + nextpos := pos + 1 + for nextpos < len(e.value) && unicode.IsMark(e.value[nextpos]) { + nextpos++ } - pos := e.cursor + rune_dx - if pos < 0 || pos >= len(e.value) { - return + for pos > 0 && unicode.IsMark(e.value[pos]) { + pos-- } - e.value = append(e.value[:pos], e.value[pos-rune_dx:]...) + + e.value = append(e.value[:pos], e.value[nextpos:]...) e.cursor = pos } From 9bb3ce57b4bbf983b5d5dbe8e84657b14869225a Mon Sep 17 00:00:00 2001 From: Uchino Jun Date: Sun, 28 Apr 2019 09:21:16 +0900 Subject: [PATCH 15/18] Substitute drawSeq function for drawch function --- up.go | 91 +++++++++++++++++++++++++++++++---------------------------- 1 file changed, 48 insertions(+), 43 deletions(-) diff --git a/up.go b/up.go index 1d34749..ea523e3 100644 --- a/up.go +++ b/up.go @@ -424,18 +424,21 @@ func (v *BufView) DrawTo(region Region) { } lclip := false - drawch := func(x, y int, mainc rune, combc []rune) { + drawSeq := func(x, y int, seq []rune) { if x <= v.X && v.X != 0 { - x, mainc, combc = 0, '«', nil + x, seq[0] = 0, '«' lclip = true } else { x -= v.X } + if x >= region.W { - x, mainc, combc = region.W-1, '»', nil + x, seq[0] = region.W-1, '»' } - region.SetContent(x, y, mainc, combc, tcell.StyleDefault) + + region.SetContent(x, y, seq[0], seq[1:], tcell.StyleDefault) } + endline := func(x, y int) { x -= v.X if x < 0 { @@ -451,11 +454,8 @@ func (v *BufView) DrawTo(region Region) { } x, y := 0, 0 - // the primary non-zero width rune - var mainc rune - // the array that follows is a possible list of combining characters to append - combc := make([]rune, 0) - + // a combining character sequence, see: http://unicode.org/faq/char_combmark.html + seq := []rune{} for { ch, _, err := r.ReadRune() if y >= region.H || err == io.EOF { @@ -464,49 +464,54 @@ func (v *BufView) DrawTo(region Region) { panic(err) } - if unicode.IsMark(ch) { - combc = append(combc, ch) - } else { - switch mainc { - case '\t': - const tabwidth = 8 - drawch(x, y, ' ', nil) - for x%tabwidth < (tabwidth - 1) { - x++ - if x >= region.W { - break + if !unicode.IsMark(ch) { + if len(seq) > 0 { + switch seq[0] { + case '\t': + const tabwidth = 8 + drawSeq(x, y, []rune{' '}) + for x%tabwidth < (tabwidth - 1) { + x++ + if x >= region.W { + break + } + drawSeq(x, y, []rune{' '}) } - drawch(x, y, ' ', nil) - } - default: - drawch(x, y, mainc, combc) - x += runewidth.RuneWidth(mainc) - if ch == '\n' { - endline(x, y) - x, y = 0, y+1 + default: + drawSeq(x, y, seq) + x += runewidth.RuneWidth(seq[0]) } + seq = seq[:0] + } + + if ch == '\n' { + endline(x, y) + x, y = 0, y+1 + continue } - mainc, combc = ch, nil } + seq = append(seq, ch) } // print the last character - switch mainc { - case '\n': - endline(x, y) - x, y = 0, y+1 - case '\t': - const tabwidth = 8 - drawch(x, y, ' ', nil) - for x%tabwidth < (tabwidth - 1) { - x++ - if x >= region.W { - break + if len(seq) > 0 { + switch seq[0] { + case '\n': + endline(x, y) + x, y = 0, y+1 + case '\t': + const tabwidth = 8 + drawSeq(x, y, []rune{' '}) + for x%tabwidth < (tabwidth - 1) { + x++ + if x >= region.W { + break + } + drawSeq(x, y, []rune{' '}) } - drawch(x, y, ' ', nil) + default: + drawSeq(x, y, seq) } - default: - drawch(x, y, mainc, combc) } for ; y < region.H; y++ { endline(x, y) From 290fae23ede4999d8f84075458767a52228c4630 Mon Sep 17 00:00:00 2001 From: Uchino Jun Date: Sun, 28 Apr 2019 09:47:25 +0900 Subject: [PATCH 16/18] Modified some comments --- up.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/up.go b/up.go index ea523e3..ab9f9a5 100644 --- a/up.go +++ b/up.go @@ -454,7 +454,7 @@ func (v *BufView) DrawTo(region Region) { } x, y := 0, 0 - // a combining character sequence, see: http://unicode.org/faq/char_combmark.html + // a possible list of combining characters seq := []rune{} for { ch, _, err := r.ReadRune() @@ -845,7 +845,7 @@ var ( func drawText(region Region, x int, style tcell.Style, text []rune) int { w := 0 - // a combining character sequence, see: http://unicode.org/faq/char_combmark.html + // a possible list of combining characters seq := []rune{} drawSeq := func() { if len(seq) > 0 { From ca16ea7a345556c6bca73a4b42e6eb7affa049c6 Mon Sep 17 00:00:00 2001 From: Uchino Jun Date: Sun, 28 Apr 2019 12:33:41 +0900 Subject: [PATCH 17/18] go fmt --- up.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/up.go b/up.go index ab9f9a5..df9df7e 100644 --- a/up.go +++ b/up.go @@ -424,7 +424,7 @@ func (v *BufView) DrawTo(region Region) { } lclip := false - drawSeq := func(x, y int, seq []rune) { + drawSeq := func(x, y int, seq []rune) { if x <= v.X && v.X != 0 { x, seq[0] = 0, '«' lclip = true @@ -454,7 +454,7 @@ func (v *BufView) DrawTo(region Region) { } x, y := 0, 0 - // a possible list of combining characters + // a possible list of combining characters seq := []rune{} for { ch, _, err := r.ReadRune() @@ -823,7 +823,7 @@ fallback_print: } type Region struct { - W, H int + W, H int SetContent func(x, y int, mainc rune, combc []rune, style tcell.Style) } @@ -845,7 +845,7 @@ var ( func drawText(region Region, x int, style tcell.Style, text []rune) int { w := 0 - // a possible list of combining characters + // a possible list of combining characters seq := []rune{} drawSeq := func() { if len(seq) > 0 { From fa748c5e69d7bb256797b65c2877f8b5e30785ec Mon Sep 17 00:00:00 2001 From: Uchino Jun Date: Sun, 28 Apr 2019 14:12:45 +0900 Subject: [PATCH 18/18] Added some tests for delete method --- up_test.go | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/up_test.go b/up_test.go index de31c59..67e0667 100644 --- a/up_test.go +++ b/up_test.go @@ -81,3 +81,74 @@ func Test_Editor_insert(test *testing.T) { } } } + +func Test_Editor_delete(test *testing.T) { + cases := []struct { + comment string + e Editor + delete int + wantValue []rune + }{ + { + comment: "delete on the first ASCII char", + e: Editor{ + value: []rune(`abc`), + cursor: 0, + }, + delete: 0, + wantValue: []rune(`bc`), + }, + { + comment: "backspace on the first ASCII char", + e: Editor{ + value: []rune(`abc`), + cursor: 0, + }, + delete: -1, + wantValue: []rune(`abc`), + }, + { + comment: "delete on a mid ASCII char", + e: Editor{ + value: []rune(`abc`), + cursor: 1, + }, + delete: 0, + wantValue: []rune(`ac`), + }, + { + comment: "backspace on a mid ASCII char", + e: Editor{ + value: []rune(`abc`), + cursor: 1, + }, + delete: -1, + wantValue: []rune(`bc`), + }, + { + comment: "erase a primary char followed by combining chars", + e: Editor{ + value: []rune(`abs̽⃝c`), + cursor: 5, + }, + delete: -1, + wantValue: []rune(`abc`), + }, + { + comment: "delete on a primary char followed by combining chars", + e: Editor{ + value: []rune(`abs̽⃝c`), + cursor: 2, + }, + delete: 0, + wantValue: []rune(`abc`), + }, + } + + for _, c := range cases { + c.e.delete(c.delete) + if string(c.e.value) != string(c.wantValue) { + test.Errorf("%q: bad value\nwant: %q\nhave: %q", c.comment, c.wantValue, c.e.value) + } + } +}