micro/internal/display/infowindow.go

293 lines
6.2 KiB
Go
Raw Normal View History

2019-01-01 06:07:01 +03:00
package display
import (
runewidth "github.com/mattn/go-runewidth"
2020-05-04 17:16:15 +03:00
"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"
2020-01-01 04:15:45 +03:00
"github.com/zyedidia/tcell"
2019-01-01 06:07:01 +03:00
)
type InfoWindow struct {
2019-01-02 06:36:12 +03:00
*info.InfoBuf
2019-01-01 06:07:01 +03:00
*View
hscroll int
2019-01-01 06:07:01 +03:00
}
2019-06-16 19:45:39 +03:00
func (i *InfoWindow) errStyle() tcell.Style {
errStyle := config.DefStyle.
Foreground(tcell.ColorBlack).
Background(tcell.ColorMaroon)
2019-01-01 06:07:01 +03:00
2019-06-16 19:45:39 +03:00
if _, ok := config.Colorscheme["error-message"]; ok {
errStyle = config.Colorscheme["error-message"]
}
return errStyle
}
func (i *InfoWindow) defStyle() tcell.Style {
defStyle := config.DefStyle
2019-01-01 06:07:01 +03:00
if _, ok := config.Colorscheme["message"]; ok {
2019-06-16 19:45:39 +03:00
defStyle = config.Colorscheme["message"]
2019-01-01 06:07:01 +03:00
}
2019-06-16 19:45:39 +03:00
return defStyle
}
2019-01-01 06:07:01 +03:00
2019-06-16 19:45:39 +03:00
func NewInfoWindow(b *info.InfoBuf) *InfoWindow {
iw := new(InfoWindow)
iw.InfoBuf = b
iw.View = new(View)
2019-01-01 06:07:01 +03:00
2019-01-17 02:37:45 +03:00
iw.Width, iw.Y = screen.Screen.Size()
iw.Y--
2019-01-01 06:07:01 +03:00
return iw
}
2019-01-05 01:40:56 +03:00
func (i *InfoWindow) Resize(w, h int) {
2019-01-17 02:37:45 +03:00
i.Width = w
i.Y = h
2019-01-05 01:40:56 +03:00
}
2019-01-14 08:57:39 +03:00
func (i *InfoWindow) SetBuffer(b *buffer.Buffer) {
i.InfoBuf.Buffer = b
}
2019-01-05 02:08:11 +03:00
func (i *InfoWindow) Relocate() bool { return false }
func (i *InfoWindow) GetView() *View { return i.View }
func (i *InfoWindow) SetView(v *View) {}
func (i *InfoWindow) SetActive(b bool) {}
2019-08-04 09:53:33 +03:00
func (i *InfoWindow) IsActive() bool { return true }
2019-01-01 06:07:01 +03:00
2019-12-25 00:01:08 +03:00
func (i *InfoWindow) LocFromVisual(vloc buffer.Loc) buffer.Loc {
2019-01-03 01:39:50 +03:00
c := i.Buffer.GetActiveCursor()
l := i.Buffer.LineBytes(0)
2020-05-20 23:47:08 +03:00
n := util.CharacterCountInString(i.Msg)
2019-01-03 01:39:50 +03:00
return buffer.Loc{c.GetCharPosInLine(l, vloc.X-n), 0}
}
2019-01-01 06:07:01 +03:00
func (i *InfoWindow) Clear() {
2019-01-17 02:37:45 +03:00
for x := 0; x < i.Width; x++ {
2020-01-02 06:40:51 +03:00
screen.SetContent(x, i.Y, ' ', nil, i.defStyle())
2019-01-01 06:07:01 +03:00
}
}
2019-01-01 07:47:24 +03:00
func (i *InfoWindow) displayBuffer() {
b := i.Buffer
line := b.LineBytes(0)
activeC := b.GetActiveCursor()
blocX := 0
2020-05-20 23:47:08 +03:00
vlocX := util.CharacterCountInString(i.Msg)
2019-01-01 07:47:24 +03:00
tabsize := 4
2019-01-03 04:07:48 +03:00
line, nColsBeforeStart, bslice := util.SliceVisualEnd(line, blocX, tabsize)
blocX = bslice
2019-01-01 07:47:24 +03:00
2020-05-20 23:43:12 +03:00
draw := func(r rune, combc []rune, style tcell.Style) {
2019-01-01 07:47:24 +03:00
if nColsBeforeStart <= 0 {
bloc := buffer.Loc{X: blocX, Y: 0}
if activeC.HasSelection() &&
(bloc.GreaterEqual(activeC.CurSelection[0]) && bloc.LessThan(activeC.CurSelection[1]) ||
bloc.LessThan(activeC.CurSelection[0]) && bloc.GreaterEqual(activeC.CurSelection[1])) {
// The current character is selected
2019-06-16 19:45:39 +03:00
style = i.defStyle().Reverse(true)
2019-01-01 07:47:24 +03:00
if s, ok := config.Colorscheme["selection"]; ok {
style = s
}
}
2019-06-15 23:54:53 +03:00
rw := runewidth.RuneWidth(r)
for j := 0; j < rw; j++ {
c := r
if j > 0 {
c = ' '
2020-05-20 23:43:12 +03:00
combc = nil
2019-06-15 23:54:53 +03:00
}
2020-05-20 23:43:12 +03:00
screen.SetContent(vlocX, i.Y, c, combc, style)
2019-06-15 23:54:53 +03:00
}
2019-01-01 07:47:24 +03:00
vlocX++
}
nColsBeforeStart--
}
totalwidth := blocX - nColsBeforeStart
for len(line) > 0 {
curVX := vlocX
curBX := blocX
2020-05-20 23:43:12 +03:00
r, combc, size := util.DecodeCharacter(line)
2019-01-01 07:47:24 +03:00
2020-05-20 23:43:12 +03:00
draw(r, combc, i.defStyle())
2019-01-01 07:47:24 +03:00
width := 0
char := ' '
switch r {
case '\t':
ts := tabsize - (totalwidth % tabsize)
width = ts
default:
width = runewidth.RuneWidth(r)
char = '@'
}
blocX++
line = line[size:]
// Draw any extra characters either spaces for tabs or @ for incomplete wide runes
if width > 1 {
for j := 1; j < width; j++ {
2020-05-20 23:43:12 +03:00
draw(char, nil, i.defStyle())
2019-01-01 07:47:24 +03:00
}
}
if activeC.X == curBX {
screen.ShowCursor(curVX, i.Y)
}
2019-01-01 07:47:24 +03:00
totalwidth += width
2019-01-17 02:37:45 +03:00
if vlocX >= i.Width {
2019-01-01 07:47:24 +03:00
break
}
}
if activeC.X == blocX {
2020-01-02 05:29:18 +03:00
screen.ShowCursor(vlocX, i.Y)
2019-01-17 02:37:45 +03:00
}
}
2019-01-21 01:49:20 +03:00
var keydisplay = []string{"^Q Quit, ^S Save, ^O Open, ^G Help, ^E Command Bar, ^K Cut Line", "^F Find, ^Z Undo, ^Y Redo, ^A Select All, ^D Duplicate Line, ^T New Tab"}
2019-01-17 02:37:45 +03:00
func (i *InfoWindow) displayKeyMenu() {
// TODO: maybe make this based on the actual keybindings
2019-01-21 01:49:20 +03:00
for y := 0; y < len(keydisplay); y++ {
2019-01-17 02:37:45 +03:00
for x := 0; x < i.Width; x++ {
2019-01-21 01:49:20 +03:00
if x < len(keydisplay[y]) {
2020-01-02 06:40:51 +03:00
screen.SetContent(x, i.Y-len(keydisplay)+y, rune(keydisplay[y][x]), nil, i.defStyle())
2019-01-17 02:37:45 +03:00
} else {
2020-01-02 06:40:51 +03:00
screen.SetContent(x, i.Y-len(keydisplay)+y, ' ', nil, i.defStyle())
2019-01-17 02:37:45 +03:00
}
}
2019-01-01 07:47:24 +03:00
}
}
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
s := i.totalSize()
for j, n := range i.Suggestions {
2020-05-20 23:47:08 +03:00
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
}
}
2019-01-01 06:07:01 +03:00
func (i *InfoWindow) Display() {
if i.HasPrompt || config.GlobalSettings["infobar"].(bool) {
i.Clear()
x := 0
if config.GetGlobalOption("keymenu").(bool) {
i.displayKeyMenu()
}
2019-01-01 07:47:24 +03:00
if !i.HasPrompt && !i.HasMessage && !i.HasError {
return
}
2019-01-15 06:16:44 +03:00
i.Clear()
2019-06-16 19:45:39 +03:00
style := i.defStyle()
2019-01-01 06:07:01 +03:00
if i.HasError {
2019-06-16 19:45:39 +03:00
style = i.errStyle()
2019-01-01 06:07:01 +03:00
}
2019-01-01 07:47:24 +03:00
display := i.Msg
2019-01-01 06:07:01 +03:00
for _, c := range display {
2020-01-02 06:40:51 +03:00
screen.SetContent(x, i.Y, c, nil, style)
2019-01-01 06:07:01 +03:00
x += runewidth.RuneWidth(c)
}
2019-01-01 07:47:24 +03:00
if i.HasPrompt {
i.displayBuffer()
}
2019-01-01 06:07:01 +03:00
}
2019-01-21 01:49:20 +03:00
2019-01-25 02:09:57 +03:00
if i.HasSuggestions && len(i.Suggestions) > 1 {
i.scrollToSuggestion()
x := -i.hscroll
done := false
2019-01-21 01:49:20 +03:00
statusLineStyle := config.DefStyle.Reverse(true)
if style, ok := config.Colorscheme["statusline"]; ok {
statusLineStyle = style
}
keymenuOffset := 0
if config.GetGlobalOption("keymenu").(bool) {
keymenuOffset = len(keydisplay)
}
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++
}
}
2019-01-25 02:09:57 +03:00
for j, s := range i.Suggestions {
style := statusLineStyle
if i.CurSuggestion == j {
style = style.Reverse(true)
}
2019-01-21 01:49:20 +03:00
for _, r := range s {
draw(r, style)
// screen.SetContent(x, i.Y-keymenuOffset-1, r, nil, style)
2019-01-21 01:49:20 +03:00
}
draw(' ', statusLineStyle)
2019-01-21 01:49:20 +03:00
}
for x < i.Width {
draw(' ', statusLineStyle)
2019-01-21 01:49:20 +03:00
}
}
2019-01-01 06:07:01 +03:00
}