]> git.lizzy.rs Git - micro.git/blobdiff - internal/display/infowindow.go
Add VLoc, VLocFromLoc and LocFromVLoc
[micro.git] / internal / display / infowindow.go
index b179c81e941978ca808573975866c81a9a8a13df..1892de39addde8eca67e42cc91ace64733077934 100644 (file)
@@ -1,20 +1,20 @@
 package display
 
 import (
-       "unicode/utf8"
-
        runewidth "github.com/mattn/go-runewidth"
-       "github.com/zyedidia/micro/internal/buffer"
-       "github.com/zyedidia/micro/internal/config"
-       "github.com/zyedidia/micro/internal/info"
-       "github.com/zyedidia/micro/internal/screen"
-       "github.com/zyedidia/micro/internal/util"
-       "github.com/zyedidia/tcell"
+       "github.com/zyedidia/micro/v2/internal/buffer"
+       "github.com/zyedidia/micro/v2/internal/config"
+       "github.com/zyedidia/micro/v2/internal/info"
+       "github.com/zyedidia/micro/v2/internal/screen"
+       "github.com/zyedidia/micro/v2/internal/util"
+       "github.com/zyedidia/tcell/v2"
 )
 
 type InfoWindow struct {
        *info.InfoBuf
        *View
+
+       hscroll int
 }
 
 func (i *InfoWindow) errStyle() tcell.Style {
@@ -68,13 +68,22 @@ func (i *InfoWindow) IsActive() bool   { return true }
 func (i *InfoWindow) LocFromVisual(vloc buffer.Loc) buffer.Loc {
        c := i.Buffer.GetActiveCursor()
        l := i.Buffer.LineBytes(0)
-       n := utf8.RuneCountInString(i.Msg)
+       n := util.CharacterCountInString(i.Msg)
        return buffer.Loc{c.GetCharPosInLine(l, vloc.X-n), 0}
 }
 
+func (i *InfoWindow) BufWidth() int  { return i.Width }
+func (i *InfoWindow) BufHeight() int { return 1 }
+
+func (i *InfoWindow) Scroll(s SLoc, n int) SLoc        { return s }
+func (i *InfoWindow) Diff(s1, s2 SLoc) int             { return 0 }
+func (i *InfoWindow) SLocFromLoc(loc buffer.Loc) SLoc  { return SLoc{0, 0} }
+func (i *InfoWindow) VLocFromLoc(loc buffer.Loc) VLoc  { return VLoc{SLoc{0, 0}, loc.X} }
+func (i *InfoWindow) LocFromVLoc(vloc VLoc) buffer.Loc { return buffer.Loc{vloc.VisualX, 0} }
+
 func (i *InfoWindow) Clear() {
        for x := 0; x < i.Width; x++ {
-               screen.Screen.SetContent(x, i.Y, ' ', nil, i.defStyle())
+               screen.SetContent(x, i.Y, ' ', nil, i.defStyle())
        }
 }
 
@@ -84,13 +93,13 @@ func (i *InfoWindow) displayBuffer() {
        activeC := b.GetActiveCursor()
 
        blocX := 0
-       vlocX := utf8.RuneCountInString(i.Msg)
+       vlocX := util.CharacterCountInString(i.Msg)
 
        tabsize := 4
        line, nColsBeforeStart, bslice := util.SliceVisualEnd(line, blocX, tabsize)
        blocX = bslice
 
-       draw := func(r rune, style tcell.Style) {
+       draw := func(r rune, combc []rune, style tcell.Style) {
                if nColsBeforeStart <= 0 {
                        bloc := buffer.Loc{X: blocX, Y: 0}
                        if activeC.HasSelection() &&
@@ -110,8 +119,9 @@ func (i *InfoWindow) displayBuffer() {
                                c := r
                                if j > 0 {
                                        c = ' '
+                                       combc = nil
                                }
-                               screen.Screen.SetContent(vlocX, i.Y, c, nil, style)
+                               screen.SetContent(vlocX, i.Y, c, combc, style)
                        }
                        vlocX++
                }
@@ -120,13 +130,11 @@ func (i *InfoWindow) displayBuffer() {
 
        totalwidth := blocX - nColsBeforeStart
        for len(line) > 0 {
-               if activeC.X == blocX {
-                       screen.Screen.ShowCursor(vlocX, i.Y)
-               }
-
-               r, size := utf8.DecodeRune(line)
+               curVX := vlocX
+               curBX := blocX
+               r, combc, size := util.DecodeCharacter(line)
 
-               draw(r, i.defStyle())
+               draw(r, combc, i.defStyle())
 
                width := 0
 
@@ -146,16 +154,19 @@ func (i *InfoWindow) displayBuffer() {
                // Draw any extra characters either spaces for tabs or @ for incomplete wide runes
                if width > 1 {
                        for j := 1; j < width; j++ {
-                               draw(char, i.defStyle())
+                               draw(char, nil, i.defStyle())
                        }
                }
+               if activeC.X == curBX {
+                       screen.ShowCursor(curVX, i.Y)
+               }
                totalwidth += width
                if vlocX >= i.Width {
                        break
                }
        }
        if activeC.X == blocX {
-               screen.Screen.ShowCursor(vlocX, i.Y)
+               screen.ShowCursor(vlocX, i.Y)
        }
 }
 
@@ -167,21 +178,52 @@ func (i *InfoWindow) displayKeyMenu() {
        for y := 0; y < len(keydisplay); y++ {
                for x := 0; x < i.Width; x++ {
                        if x < len(keydisplay[y]) {
-                               screen.Screen.SetContent(x, i.Y-len(keydisplay)+y, rune(keydisplay[y][x]), nil, i.defStyle())
+                               screen.SetContent(x, i.Y-len(keydisplay)+y, rune(keydisplay[y][x]), nil, i.defStyle())
                        } else {
-                               screen.Screen.SetContent(x, i.Y-len(keydisplay)+y, ' ', nil, i.defStyle())
+                               screen.SetContent(x, i.Y-len(keydisplay)+y, ' ', nil, i.defStyle())
                        }
                }
        }
 }
 
-func (i *InfoWindow) Display() {
+func (i *InfoWindow) totalSize() int {
+       sum := 0
+       for _, n := range i.Suggestions {
+               sum += runewidth.StringWidth(n) + 1
+       }
+       return sum
+}
+
+func (i *InfoWindow) scrollToSuggestion() {
        x := 0
-       if config.GetGlobalOption("keymenu").(bool) {
-               i.displayKeyMenu()
+       s := i.totalSize()
+
+       for j, n := range i.Suggestions {
+               c := util.CharacterCountInString(n)
+               if j == i.CurSuggestion {
+                       if x+c >= i.hscroll+i.Width {
+                               i.hscroll = util.Clamp(x+c+1-i.Width, 0, s-i.Width)
+                       } else if x < i.hscroll {
+                               i.hscroll = util.Clamp(x-1, 0, s-i.Width)
+                       }
+                       break
+               }
+               x += c + 1
+       }
+
+       if s-i.Width <= 0 {
+               i.hscroll = 0
        }
+}
 
+func (i *InfoWindow) Display() {
        if i.HasPrompt || config.GlobalSettings["infobar"].(bool) {
+               i.Clear()
+               x := 0
+               if config.GetGlobalOption("keymenu").(bool) {
+                       i.displayKeyMenu()
+               }
+
                if !i.HasPrompt && !i.HasMessage && !i.HasError {
                        return
                }
@@ -194,7 +236,7 @@ func (i *InfoWindow) Display() {
 
                display := i.Msg
                for _, c := range display {
-                       screen.Screen.SetContent(x, i.Y, c, nil, style)
+                       screen.SetContent(x, i.Y, c, nil, style)
                        x += runewidth.RuneWidth(c)
                }
 
@@ -204,6 +246,11 @@ func (i *InfoWindow) Display() {
        }
 
        if i.HasSuggestions && len(i.Suggestions) > 1 {
+               i.scrollToSuggestion()
+
+               x := -i.hscroll
+               done := false
+
                statusLineStyle := config.DefStyle.Reverse(true)
                if style, ok := config.Colorscheme["statusline"]; ok {
                        statusLineStyle = style
@@ -212,29 +259,43 @@ func (i *InfoWindow) Display() {
                if config.GetGlobalOption("keymenu").(bool) {
                        keymenuOffset = len(keydisplay)
                }
-               x := 0
+
+               draw := func(r rune, s tcell.Style) {
+                       y := i.Y - keymenuOffset - 1
+                       rw := runewidth.RuneWidth(r)
+                       for j := 0; j < rw; j++ {
+                               c := r
+                               if j > 0 {
+                                       c = ' '
+                               }
+
+                               if x == i.Width-1 && !done {
+                                       screen.SetContent(i.Width-1, y, '>', nil, s)
+                                       x++
+                                       break
+                               } else if x == 0 && i.hscroll > 0 {
+                                       screen.SetContent(0, y, '<', nil, s)
+                               } else if x >= 0 && x < i.Width {
+                                       screen.SetContent(x, y, c, nil, s)
+                               }
+                               x++
+                       }
+               }
+
                for j, s := range i.Suggestions {
                        style := statusLineStyle
                        if i.CurSuggestion == j {
                                style = style.Reverse(true)
                        }
                        for _, r := range s {
-                               screen.Screen.SetContent(x, i.Y-keymenuOffset-1, r, nil, style)
-                               x++
-                               if x >= i.Width {
-                                       return
-                               }
-                       }
-                       screen.Screen.SetContent(x, i.Y-keymenuOffset-1, ' ', nil, statusLineStyle)
-                       x++
-                       if x >= i.Width {
-                               return
+                               draw(r, style)
+                               // screen.SetContent(x, i.Y-keymenuOffset-1, r, nil, style)
                        }
+                       draw(' ', statusLineStyle)
                }
 
                for x < i.Width {
-                       screen.Screen.SetContent(x, i.Y-keymenuOffset-1, ' ', nil, statusLineStyle)
-                       x++
+                       draw(' ', statusLineStyle)
                }
        }
 }