micro/internal/display/statusline.go

206 lines
4.9 KiB
Go
Raw Normal View History

2018-08-28 22:44:52 +00:00
package display
2016-03-17 22:20:07 +00:00
import (
2018-08-26 03:06:44 +00:00
"bytes"
"fmt"
"regexp"
2016-03-17 22:20:07 +00:00
"strconv"
"strings"
2018-08-27 19:53:10 +00:00
luar "layeh.com/gopher-luar"
2019-06-15 20:54:53 +00:00
runewidth "github.com/mattn/go-runewidth"
lua "github.com/yuin/gopher-lua"
2020-05-04 14:16:15 +00:00
"github.com/zyedidia/micro/v2/internal/buffer"
"github.com/zyedidia/micro/v2/internal/config"
ulua "github.com/zyedidia/micro/v2/internal/lua"
"github.com/zyedidia/micro/v2/internal/screen"
"github.com/zyedidia/micro/v2/internal/util"
2016-03-17 22:20:07 +00:00
)
2018-08-26 03:06:44 +00:00
// StatusLine represents the information line at the bottom
// of each window
2016-03-25 16:14:22 +00:00
// It gives information such as filename, whether the file has been
// modified, filetype, cursor location
2018-08-26 03:06:44 +00:00
type StatusLine struct {
2019-06-15 20:13:04 +00:00
Info map[string]func(*buffer.Buffer) string
2016-03-17 22:20:07 +00:00
2018-08-28 22:44:52 +00:00
win *BufWindow
2018-08-26 03:06:44 +00:00
}
var statusInfo = map[string]func(*buffer.Buffer) string{
"filename": func(b *buffer.Buffer) string {
return b.GetName()
},
"line": func(b *buffer.Buffer) string {
return strconv.Itoa(b.GetActiveCursor().Y + 1)
},
"col": func(b *buffer.Buffer) string {
return strconv.Itoa(b.GetActiveCursor().X + 1)
},
"modified": func(b *buffer.Buffer) string {
if b.Modified() {
return "+ "
}
if b.Type.Readonly {
return "[ro] "
}
return ""
},
}
2019-12-26 17:46:10 +00:00
func SetStatusInfoFnLua(fn string) {
luaFn := strings.Split(fn, ".")
2019-08-26 18:47:27 +00:00
if len(luaFn) <= 1 {
return
}
plName, plFn := luaFn[0], luaFn[1]
2019-08-03 01:29:47 +00:00
pl := config.FindPlugin(plName)
2019-08-26 18:47:27 +00:00
if pl == nil {
return
}
2019-12-26 17:46:10 +00:00
statusInfo[fn] = func(b *buffer.Buffer) string {
if pl == nil || !pl.IsEnabled() {
return ""
}
val, err := pl.Call(plFn, luar.New(ulua.L, b))
if err == nil {
if v, ok := val.(lua.LString); !ok {
screen.TermMessage(plFn, "should return a string")
return ""
} else {
return string(v)
}
}
return ""
}
}
2018-08-26 03:06:44 +00:00
// NewStatusLine returns a statusline bound to a window
2018-08-28 22:44:52 +00:00
func NewStatusLine(win *BufWindow) *StatusLine {
2018-08-26 03:06:44 +00:00
s := new(StatusLine)
s.win = win
return s
}
2016-03-25 16:14:22 +00:00
2018-08-26 03:06:44 +00:00
// FindOpt finds a given option in the current buffer's settings
func (s *StatusLine) FindOpt(opt string) interface{} {
if val, ok := s.win.Buf.Settings[opt]; ok {
return val
2016-03-17 22:20:07 +00:00
}
2018-08-26 03:06:44 +00:00
return "null"
}
2016-03-25 16:14:22 +00:00
2018-08-26 03:06:44 +00:00
var formatParser = regexp.MustCompile(`\$\(.+?\)`)
2016-03-25 16:14:22 +00:00
2018-08-26 03:06:44 +00:00
// Display draws the statusline to the screen
func (s *StatusLine) Display() {
// We'll draw the line at the lowest line in the window
2018-08-27 19:53:10 +00:00
y := s.win.Height + s.win.Y - 1
2019-06-16 19:56:39 +00:00
b := s.win.Buf
// autocomplete suggestions (for the buffer, not for the infowindow)
2019-06-16 19:56:39 +00:00
if b.HasSuggestions && len(b.Suggestions) > 1 {
statusLineStyle := config.DefStyle.Reverse(true)
if style, ok := config.Colorscheme["statusline"]; ok {
statusLineStyle = style
}
keymenuOffset := 0
if config.GetGlobalOption("keymenu").(bool) {
keymenuOffset = len(keydisplay)
}
x := 0
for j, sug := range b.Suggestions {
style := statusLineStyle
if b.CurSuggestion == j {
style = style.Reverse(true)
}
for _, r := range sug {
2020-01-02 03:40:51 +00:00
screen.SetContent(x, y-keymenuOffset, r, nil, style)
2019-06-16 19:56:39 +00:00
x++
if x >= s.win.Width {
return
}
}
2020-01-02 03:40:51 +00:00
screen.SetContent(x, y-keymenuOffset, ' ', nil, statusLineStyle)
2019-06-16 19:56:39 +00:00
x++
if x >= s.win.Width {
return
}
}
for x < s.win.Width {
2020-01-02 03:40:51 +00:00
screen.SetContent(x, y-keymenuOffset, ' ', nil, statusLineStyle)
2019-06-16 19:56:39 +00:00
x++
}
return
}
2018-08-26 03:06:44 +00:00
formatter := func(match []byte) []byte {
name := match[2 : len(match)-1]
if bytes.HasPrefix(name, []byte("opt")) {
option := name[4:]
return []byte(fmt.Sprint(s.FindOpt(string(option))))
} else if bytes.HasPrefix(name, []byte("bind")) {
2019-01-11 20:33:16 +00:00
binding := string(name[5:])
for k, v := range config.Bindings {
if v == binding {
return []byte(k)
}
}
2018-08-27 19:53:10 +00:00
return []byte("null")
2018-08-26 03:06:44 +00:00
} else {
if fn, ok := statusInfo[string(name)]; ok {
return []byte(fn(s.win.Buf))
}
return []byte{}
}
2016-04-19 17:58:02 +00:00
}
2016-03-27 21:53:00 +00:00
leftText := []byte(s.win.Buf.Settings["statusformatl"].(string))
2019-06-15 20:13:04 +00:00
leftText = formatParser.ReplaceAllFunc(leftText, formatter)
rightText := []byte(s.win.Buf.Settings["statusformatr"].(string))
2019-06-15 20:13:04 +00:00
rightText = formatParser.ReplaceAllFunc(rightText, formatter)
2018-08-26 03:06:44 +00:00
2018-08-27 19:53:10 +00:00
statusLineStyle := config.DefStyle.Reverse(true)
if style, ok := config.Colorscheme["statusline"]; ok {
statusLineStyle = style
}
2016-03-17 22:20:07 +00:00
2020-05-20 20:47:08 +00:00
leftLen := util.StringWidth(leftText, util.CharacterCount(leftText), 1)
rightLen := util.StringWidth(rightText, util.CharacterCount(rightText), 1)
2018-08-26 03:06:44 +00:00
winX := s.win.X
for x := 0; x < s.win.Width; x++ {
if x < leftLen {
2020-05-20 20:43:12 +00:00
r, combc, size := util.DecodeCharacter(leftText)
2018-08-26 03:06:44 +00:00
leftText = leftText[size:]
2019-06-15 20:54:53 +00:00
rw := runewidth.RuneWidth(r)
for j := 0; j < rw; j++ {
c := r
if j > 0 {
c = ' '
2020-05-20 20:43:12 +00:00
combc = nil
2019-06-15 20:54:53 +00:00
x++
}
2020-05-20 20:43:12 +00:00
screen.SetContent(winX+x, y, c, combc, statusLineStyle)
2019-06-15 20:54:53 +00:00
}
2018-08-26 03:06:44 +00:00
} else if x >= s.win.Width-rightLen && x < rightLen+s.win.Width-rightLen {
2020-05-20 20:43:12 +00:00
r, combc, size := util.DecodeCharacter(rightText)
2018-08-26 03:06:44 +00:00
rightText = rightText[size:]
2019-06-15 20:54:53 +00:00
rw := runewidth.RuneWidth(r)
for j := 0; j < rw; j++ {
c := r
if j > 0 {
c = ' '
2020-05-20 20:43:12 +00:00
combc = nil
2019-06-15 20:54:53 +00:00
x++
}
2020-05-20 20:43:12 +00:00
screen.SetContent(winX+x, y, c, combc, statusLineStyle)
2019-06-15 20:54:53 +00:00
}
2016-03-17 22:20:07 +00:00
} else {
2020-01-02 03:40:51 +00:00
screen.SetContent(winX+x, y, ' ', nil, statusLineStyle)
2016-03-17 22:20:07 +00:00
}
}
}