2019-01-14 01:22:11 +03:00
|
|
|
package display
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strconv"
|
|
|
|
"unicode/utf8"
|
|
|
|
|
|
|
|
runewidth "github.com/mattn/go-runewidth"
|
2019-02-04 07:17:24 +03:00
|
|
|
"github.com/zyedidia/micro/internal/buffer"
|
|
|
|
"github.com/zyedidia/micro/internal/config"
|
|
|
|
"github.com/zyedidia/micro/internal/screen"
|
|
|
|
"github.com/zyedidia/micro/internal/util"
|
2020-01-02 04:47:05 +03:00
|
|
|
"github.com/zyedidia/tcell"
|
2019-01-14 01:22:11 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
// The BufWindow provides a way of displaying a certain section
|
|
|
|
// of a buffer
|
|
|
|
type BufWindow struct {
|
|
|
|
*View
|
|
|
|
|
|
|
|
// Buffer being shown in this window
|
|
|
|
Buf *buffer.Buffer
|
|
|
|
|
|
|
|
active bool
|
|
|
|
|
|
|
|
sline *StatusLine
|
|
|
|
|
2019-12-25 00:01:08 +03:00
|
|
|
gutterOffset int
|
|
|
|
drawStatus bool
|
2019-01-14 01:22:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewBufWindow creates a new window at a location in the screen with a width and height
|
|
|
|
func NewBufWindow(x, y, width, height int, buf *buffer.Buffer) *BufWindow {
|
|
|
|
w := new(BufWindow)
|
|
|
|
w.View = new(View)
|
|
|
|
w.X, w.Y, w.Width, w.Height, w.Buf = x, y, width, height, buf
|
|
|
|
w.active = true
|
|
|
|
|
|
|
|
w.sline = NewStatusLine(w)
|
|
|
|
|
|
|
|
return w
|
|
|
|
}
|
|
|
|
|
2019-01-14 08:57:39 +03:00
|
|
|
func (w *BufWindow) SetBuffer(b *buffer.Buffer) {
|
|
|
|
w.Buf = b
|
|
|
|
}
|
|
|
|
|
2019-12-30 06:02:14 +03:00
|
|
|
func (w *BufWindow) GetView() *View {
|
|
|
|
return w.View
|
2019-01-14 01:22:11 +03:00
|
|
|
}
|
|
|
|
|
2019-12-30 06:02:14 +03:00
|
|
|
func (w *BufWindow) SetView(view *View) {
|
|
|
|
w.View = view
|
2019-01-14 01:22:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (w *BufWindow) Resize(width, height int) {
|
|
|
|
w.Width, w.Height = width, height
|
2019-12-24 00:16:54 +03:00
|
|
|
w.Relocate()
|
2019-01-14 01:22:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (w *BufWindow) SetActive(b bool) {
|
|
|
|
w.active = b
|
|
|
|
}
|
|
|
|
|
2019-08-04 09:53:33 +03:00
|
|
|
func (w *BufWindow) IsActive() bool {
|
|
|
|
return w.active
|
|
|
|
}
|
|
|
|
|
2019-01-14 01:22:11 +03:00
|
|
|
func (w *BufWindow) getStartInfo(n, lineN int) ([]byte, int, int, *tcell.Style) {
|
|
|
|
tabsize := util.IntOpt(w.Buf.Settings["tabsize"])
|
|
|
|
width := 0
|
|
|
|
bloc := buffer.Loc{0, lineN}
|
|
|
|
b := w.Buf.LineBytes(lineN)
|
|
|
|
curStyle := config.DefStyle
|
|
|
|
var s *tcell.Style
|
|
|
|
for len(b) > 0 {
|
|
|
|
r, size := utf8.DecodeRune(b)
|
|
|
|
|
|
|
|
curStyle, found := w.getStyle(curStyle, bloc, r)
|
|
|
|
if found {
|
|
|
|
s = &curStyle
|
|
|
|
}
|
|
|
|
|
|
|
|
w := 0
|
|
|
|
switch r {
|
|
|
|
case '\t':
|
|
|
|
ts := tabsize - (width % tabsize)
|
|
|
|
w = ts
|
|
|
|
default:
|
|
|
|
w = runewidth.RuneWidth(r)
|
|
|
|
}
|
|
|
|
if width+w > n {
|
|
|
|
return b, n - width, bloc.X, s
|
|
|
|
}
|
|
|
|
width += w
|
|
|
|
b = b[size:]
|
|
|
|
bloc.X++
|
|
|
|
}
|
|
|
|
return b, n - width, bloc.X, s
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear resets all cells in this window to the default style
|
|
|
|
func (w *BufWindow) Clear() {
|
|
|
|
for y := 0; y < w.Height; y++ {
|
|
|
|
for x := 0; x < w.Width; x++ {
|
2020-01-02 06:40:51 +03:00
|
|
|
screen.SetContent(w.X+x, w.Y+y, ' ', nil, config.DefStyle)
|
2019-01-14 01:22:11 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bottomline returns the line number of the lowest line in the view
|
|
|
|
// You might think that this is obviously just v.StartLine + v.Height
|
|
|
|
// but if softwrap is enabled things get complicated since one buffer
|
|
|
|
// line can take up multiple lines in the view
|
|
|
|
func (w *BufWindow) Bottomline() int {
|
2019-12-25 00:01:08 +03:00
|
|
|
if !w.Buf.Settings["softwrap"].(bool) {
|
2019-12-23 23:36:53 +03:00
|
|
|
h := w.StartLine + w.Height - 1
|
|
|
|
if w.drawStatus {
|
|
|
|
h--
|
|
|
|
}
|
|
|
|
return h
|
2019-12-23 01:24:00 +03:00
|
|
|
}
|
2019-01-14 01:22:11 +03:00
|
|
|
|
2019-12-26 03:44:58 +03:00
|
|
|
l := w.LocFromVisual(buffer.Loc{0, w.Y + w.Height})
|
2019-12-25 00:01:08 +03:00
|
|
|
|
|
|
|
return l.Y
|
2019-01-14 01:22:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Relocate moves the view window so that the cursor is in view
|
|
|
|
// This is useful if the user has scrolled far away, and then starts typing
|
|
|
|
// Returns true if the window location is moved
|
|
|
|
func (w *BufWindow) Relocate() bool {
|
|
|
|
b := w.Buf
|
2019-01-25 06:28:03 +03:00
|
|
|
// how many buffer lines are in the view
|
2019-01-14 01:22:11 +03:00
|
|
|
height := w.Bottomline() + 1 - w.StartLine
|
2019-01-15 06:16:44 +03:00
|
|
|
h := w.Height
|
|
|
|
if w.drawStatus {
|
|
|
|
h--
|
|
|
|
}
|
2019-12-25 00:01:08 +03:00
|
|
|
if b.LinesNum() <= h {
|
2019-01-15 06:16:44 +03:00
|
|
|
height = w.Height
|
2019-01-14 02:34:12 +03:00
|
|
|
}
|
2019-01-14 01:22:11 +03:00
|
|
|
ret := false
|
|
|
|
activeC := w.Buf.GetActiveCursor()
|
|
|
|
cy := activeC.Y
|
|
|
|
scrollmargin := int(b.Settings["scrollmargin"].(float64))
|
|
|
|
if cy < w.StartLine+scrollmargin && cy > scrollmargin-1 {
|
|
|
|
w.StartLine = cy - scrollmargin
|
|
|
|
ret = true
|
|
|
|
} else if cy < w.StartLine {
|
|
|
|
w.StartLine = cy
|
|
|
|
ret = true
|
|
|
|
}
|
|
|
|
if cy > w.StartLine+height-1-scrollmargin && cy < b.LinesNum()-scrollmargin {
|
|
|
|
w.StartLine = cy - height + 1 + scrollmargin
|
|
|
|
ret = true
|
|
|
|
} else if cy >= b.LinesNum()-scrollmargin && cy >= height {
|
|
|
|
w.StartLine = b.LinesNum() - height
|
|
|
|
ret = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// horizontal relocation (scrolling)
|
|
|
|
if !b.Settings["softwrap"].(bool) {
|
|
|
|
cx := activeC.GetVisualX()
|
|
|
|
if cx < w.StartCol {
|
|
|
|
w.StartCol = cx
|
|
|
|
ret = true
|
|
|
|
}
|
|
|
|
if cx+w.gutterOffset+1 > w.StartCol+w.Width {
|
|
|
|
w.StartCol = cx - w.Width + w.gutterOffset + 1
|
|
|
|
ret = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2019-12-25 00:01:08 +03:00
|
|
|
// LocFromVisual takes a visual location (x and y position) and returns the
|
|
|
|
// position in the buffer corresponding to the visual location
|
|
|
|
// Computing the buffer location requires essentially drawing the entire screen
|
|
|
|
// to account for complications like softwrap, wide characters, and horizontal scrolling
|
|
|
|
// If the requested position does not correspond to a buffer location it returns
|
|
|
|
// the nearest position
|
|
|
|
func (w *BufWindow) LocFromVisual(svloc buffer.Loc) buffer.Loc {
|
2019-01-14 01:22:11 +03:00
|
|
|
b := w.Buf
|
|
|
|
|
|
|
|
hasMessage := len(b.Messages) > 0
|
|
|
|
bufHeight := w.Height
|
2019-01-15 06:16:44 +03:00
|
|
|
if w.drawStatus {
|
2019-01-14 01:22:11 +03:00
|
|
|
bufHeight--
|
|
|
|
}
|
|
|
|
|
2020-01-05 23:02:52 +03:00
|
|
|
bufWidth := w.Width
|
|
|
|
if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height {
|
|
|
|
bufWidth--
|
|
|
|
}
|
|
|
|
|
2019-01-14 01:22:11 +03:00
|
|
|
// We need to know the string length of the largest line number
|
|
|
|
// so we can pad appropriately when displaying line numbers
|
|
|
|
maxLineNumLength := len(strconv.Itoa(b.LinesNum()))
|
|
|
|
|
|
|
|
tabsize := int(b.Settings["tabsize"].(float64))
|
|
|
|
softwrap := b.Settings["softwrap"].(bool)
|
|
|
|
|
|
|
|
// this represents the current draw position
|
|
|
|
// within the current window
|
|
|
|
vloc := buffer.Loc{X: 0, Y: 0}
|
|
|
|
|
|
|
|
// this represents the current draw position in the buffer (char positions)
|
|
|
|
bloc := buffer.Loc{X: -1, Y: w.StartLine}
|
|
|
|
|
|
|
|
for vloc.Y = 0; vloc.Y < bufHeight; vloc.Y++ {
|
|
|
|
vloc.X = 0
|
|
|
|
if hasMessage {
|
|
|
|
vloc.X += 2
|
|
|
|
}
|
|
|
|
if b.Settings["ruler"].(bool) {
|
|
|
|
vloc.X += maxLineNumLength + 1
|
|
|
|
}
|
|
|
|
|
|
|
|
line := b.LineBytes(bloc.Y)
|
|
|
|
line, nColsBeforeStart, bslice := util.SliceVisualEnd(line, w.StartCol, tabsize)
|
|
|
|
bloc.X = bslice
|
|
|
|
|
|
|
|
draw := func() {
|
|
|
|
if nColsBeforeStart <= 0 {
|
|
|
|
vloc.X++
|
|
|
|
}
|
|
|
|
nColsBeforeStart--
|
|
|
|
}
|
|
|
|
|
|
|
|
totalwidth := w.StartCol - nColsBeforeStart
|
|
|
|
|
|
|
|
if svloc.X <= vloc.X+w.X && vloc.Y+w.Y == svloc.Y {
|
|
|
|
return bloc
|
|
|
|
}
|
|
|
|
for len(line) > 0 {
|
|
|
|
if vloc.X+w.X == svloc.X && vloc.Y+w.Y == svloc.Y {
|
|
|
|
return bloc
|
|
|
|
}
|
|
|
|
|
|
|
|
r, size := utf8.DecodeRune(line)
|
|
|
|
draw()
|
|
|
|
width := 0
|
|
|
|
|
|
|
|
switch r {
|
|
|
|
case '\t':
|
|
|
|
ts := tabsize - (totalwidth % tabsize)
|
|
|
|
width = ts
|
|
|
|
default:
|
|
|
|
width = runewidth.RuneWidth(r)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw any extra characters either spaces for tabs or @ for incomplete wide runes
|
|
|
|
if width > 1 {
|
|
|
|
for i := 1; i < width; i++ {
|
|
|
|
if vloc.X+w.X == svloc.X && vloc.Y+w.Y == svloc.Y {
|
|
|
|
return bloc
|
|
|
|
}
|
|
|
|
draw()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bloc.X++
|
|
|
|
line = line[size:]
|
|
|
|
|
|
|
|
totalwidth += width
|
|
|
|
|
|
|
|
// If we reach the end of the window then we either stop or we wrap for softwrap
|
2020-01-05 23:02:52 +03:00
|
|
|
if vloc.X >= bufWidth {
|
2019-01-14 01:22:11 +03:00
|
|
|
if !softwrap {
|
|
|
|
break
|
|
|
|
} else {
|
|
|
|
vloc.Y++
|
|
|
|
if vloc.Y >= bufHeight {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
vloc.X = 0
|
|
|
|
// This will draw an empty line number because the current line is wrapped
|
2020-01-06 04:32:29 +03:00
|
|
|
if b.Settings["ruler"].(bool) {
|
|
|
|
vloc.X += maxLineNumLength + 1
|
|
|
|
}
|
2019-01-14 01:22:11 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if vloc.Y+w.Y == svloc.Y {
|
|
|
|
return bloc
|
|
|
|
}
|
|
|
|
|
2019-12-25 00:01:08 +03:00
|
|
|
if bloc.Y+1 >= b.LinesNum() || vloc.Y+1 >= bufHeight {
|
|
|
|
return bloc
|
|
|
|
}
|
|
|
|
|
2019-01-14 01:22:11 +03:00
|
|
|
bloc.X = w.StartCol
|
|
|
|
bloc.Y++
|
|
|
|
}
|
|
|
|
|
|
|
|
return buffer.Loc{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *BufWindow) drawGutter(vloc *buffer.Loc, bloc *buffer.Loc) {
|
|
|
|
char := ' '
|
|
|
|
s := config.DefStyle
|
|
|
|
for _, m := range w.Buf.Messages {
|
|
|
|
if m.Start.Y == bloc.Y || m.End.Y == bloc.Y {
|
|
|
|
s = m.Style()
|
|
|
|
char = '>'
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2020-01-02 06:40:51 +03:00
|
|
|
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s)
|
2019-01-14 01:22:11 +03:00
|
|
|
vloc.X++
|
2020-01-02 06:40:51 +03:00
|
|
|
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s)
|
2019-01-14 01:22:11 +03:00
|
|
|
vloc.X++
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *BufWindow) drawLineNum(lineNumStyle tcell.Style, softwrapped bool, maxLineNumLength int, vloc *buffer.Loc, bloc *buffer.Loc) {
|
|
|
|
lineNum := strconv.Itoa(bloc.Y + 1)
|
|
|
|
|
|
|
|
// Write the spaces before the line number if necessary
|
|
|
|
for i := 0; i < maxLineNumLength-len(lineNum); i++ {
|
2020-01-02 06:40:51 +03:00
|
|
|
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
|
2019-01-14 01:22:11 +03:00
|
|
|
vloc.X++
|
|
|
|
}
|
|
|
|
// Write the actual line number
|
|
|
|
for _, ch := range lineNum {
|
|
|
|
if softwrapped {
|
2020-01-02 06:40:51 +03:00
|
|
|
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
|
2019-01-14 01:22:11 +03:00
|
|
|
} else {
|
2020-01-02 06:40:51 +03:00
|
|
|
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ch, nil, lineNumStyle)
|
2019-01-14 01:22:11 +03:00
|
|
|
}
|
|
|
|
vloc.X++
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write the extra space
|
2020-01-02 06:40:51 +03:00
|
|
|
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
|
2019-01-14 01:22:11 +03:00
|
|
|
vloc.X++
|
|
|
|
}
|
|
|
|
|
|
|
|
// getStyle returns the highlight style for the given character position
|
|
|
|
// If there is no change to the current highlight style it just returns that
|
|
|
|
func (w *BufWindow) getStyle(style tcell.Style, bloc buffer.Loc, r rune) (tcell.Style, bool) {
|
|
|
|
if group, ok := w.Buf.Match(bloc.Y)[bloc.X]; ok {
|
|
|
|
s := config.GetColor(group.String())
|
|
|
|
return s, true
|
|
|
|
}
|
|
|
|
return style, false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *BufWindow) showCursor(x, y int, main bool) {
|
|
|
|
if w.active {
|
2020-01-02 05:29:18 +03:00
|
|
|
if main {
|
|
|
|
screen.ShowCursor(x, y)
|
2019-01-14 01:22:11 +03:00
|
|
|
} else {
|
2020-01-02 06:40:51 +03:00
|
|
|
screen.ShowFakeCursorMulti(x, y)
|
2019-01-14 01:22:11 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// displayBuffer draws the buffer being shown in this window on the screen.Screen
|
|
|
|
func (w *BufWindow) displayBuffer() {
|
|
|
|
b := w.Buf
|
|
|
|
|
2020-01-29 01:15:02 +03:00
|
|
|
if w.Height <= 0 || w.Width <= 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-01-14 01:22:11 +03:00
|
|
|
hasMessage := len(b.Messages) > 0
|
|
|
|
bufHeight := w.Height
|
2019-01-15 06:16:44 +03:00
|
|
|
if w.drawStatus {
|
2019-01-14 01:22:11 +03:00
|
|
|
bufHeight--
|
|
|
|
}
|
|
|
|
|
2019-08-31 23:44:35 +03:00
|
|
|
bufWidth := w.Width
|
|
|
|
if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height {
|
|
|
|
bufWidth--
|
|
|
|
}
|
|
|
|
|
2019-01-14 01:22:11 +03:00
|
|
|
if b.Settings["syntax"].(bool) && b.SyntaxDef != nil {
|
2020-01-21 07:43:33 +03:00
|
|
|
for _, r := range b.Modifications {
|
2020-01-29 01:15:02 +03:00
|
|
|
final := -1
|
2020-01-21 07:43:33 +03:00
|
|
|
for i := r.X; i <= r.Y; i++ {
|
2020-01-29 01:15:02 +03:00
|
|
|
final = util.Max(b.Highlighter.ReHighlightStates(b, i), final)
|
2020-01-21 07:43:33 +03:00
|
|
|
}
|
2020-01-29 01:15:02 +03:00
|
|
|
b.Highlighter.HighlightMatches(b, r.X, final+1)
|
2019-12-23 23:36:53 +03:00
|
|
|
}
|
2020-01-21 07:43:33 +03:00
|
|
|
b.ClearModifications()
|
2019-01-14 01:22:11 +03:00
|
|
|
}
|
|
|
|
|
2019-08-05 01:11:09 +03:00
|
|
|
var matchingBraces []buffer.Loc
|
|
|
|
// bracePairs is defined in buffer.go
|
|
|
|
if b.Settings["matchbrace"].(bool) {
|
|
|
|
for _, bp := range buffer.BracePairs {
|
|
|
|
for _, c := range b.GetCursors() {
|
|
|
|
if c.HasSelection() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
curX := c.X
|
|
|
|
curLoc := c.Loc
|
|
|
|
|
|
|
|
r := c.RuneUnder(curX)
|
|
|
|
rl := c.RuneUnder(curX - 1)
|
|
|
|
if r == bp[0] || r == bp[1] || rl == bp[0] || rl == bp[1] {
|
2019-08-05 01:17:29 +03:00
|
|
|
mb, left := b.FindMatchingBrace(bp, curLoc)
|
2019-08-05 01:11:09 +03:00
|
|
|
matchingBraces = append(matchingBraces, mb)
|
2019-08-05 01:17:29 +03:00
|
|
|
if !left {
|
|
|
|
matchingBraces = append(matchingBraces, curLoc)
|
|
|
|
} else {
|
|
|
|
matchingBraces = append(matchingBraces, curLoc.Move(-1, b))
|
|
|
|
}
|
2019-08-05 01:11:09 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-14 01:22:11 +03:00
|
|
|
lineNumStyle := config.DefStyle
|
|
|
|
if style, ok := config.Colorscheme["line-number"]; ok {
|
|
|
|
lineNumStyle = style
|
|
|
|
}
|
|
|
|
curNumStyle := config.DefStyle
|
|
|
|
if style, ok := config.Colorscheme["current-line-number"]; ok {
|
|
|
|
curNumStyle = style
|
2020-01-31 23:05:55 +03:00
|
|
|
if !b.Settings["cursorline"].(bool) {
|
|
|
|
_, bg, _ := lineNumStyle.Decompose()
|
|
|
|
curNumStyle = curNumStyle.Background(bg)
|
|
|
|
}
|
2019-01-14 01:22:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// We need to know the string length of the largest line number
|
|
|
|
// so we can pad appropriately when displaying line numbers
|
|
|
|
maxLineNumLength := len(strconv.Itoa(b.LinesNum()))
|
|
|
|
|
|
|
|
softwrap := b.Settings["softwrap"].(bool)
|
2019-01-21 07:45:42 +03:00
|
|
|
tabsize := util.IntOpt(b.Settings["tabsize"])
|
|
|
|
colorcolumn := util.IntOpt(b.Settings["colorcolumn"])
|
2019-01-14 01:22:11 +03:00
|
|
|
|
|
|
|
// this represents the current draw position
|
|
|
|
// within the current window
|
|
|
|
vloc := buffer.Loc{X: 0, Y: 0}
|
|
|
|
|
|
|
|
// this represents the current draw position in the buffer (char positions)
|
|
|
|
bloc := buffer.Loc{X: -1, Y: w.StartLine}
|
|
|
|
|
|
|
|
cursors := b.GetCursors()
|
|
|
|
|
|
|
|
curStyle := config.DefStyle
|
|
|
|
for vloc.Y = 0; vloc.Y < bufHeight; vloc.Y++ {
|
|
|
|
vloc.X = 0
|
|
|
|
|
|
|
|
if hasMessage {
|
|
|
|
w.drawGutter(&vloc, &bloc)
|
|
|
|
}
|
|
|
|
|
|
|
|
if b.Settings["ruler"].(bool) {
|
|
|
|
s := lineNumStyle
|
|
|
|
for _, c := range cursors {
|
|
|
|
if bloc.Y == c.Y && w.active {
|
|
|
|
s = curNumStyle
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
w.drawLineNum(s, false, maxLineNumLength, &vloc, &bloc)
|
|
|
|
}
|
|
|
|
|
|
|
|
w.gutterOffset = vloc.X
|
|
|
|
|
|
|
|
line, nColsBeforeStart, bslice, startStyle := w.getStartInfo(w.StartCol, bloc.Y)
|
|
|
|
if startStyle != nil {
|
|
|
|
curStyle = *startStyle
|
|
|
|
}
|
|
|
|
bloc.X = bslice
|
|
|
|
|
|
|
|
draw := func(r rune, style tcell.Style, showcursor bool) {
|
|
|
|
if nColsBeforeStart <= 0 {
|
|
|
|
for _, c := range cursors {
|
|
|
|
if c.HasSelection() &&
|
|
|
|
(bloc.GreaterEqual(c.CurSelection[0]) && bloc.LessThan(c.CurSelection[1]) ||
|
|
|
|
bloc.LessThan(c.CurSelection[0]) && bloc.GreaterEqual(c.CurSelection[1])) {
|
|
|
|
// The current character is selected
|
|
|
|
style = config.DefStyle.Reverse(true)
|
|
|
|
|
|
|
|
if s, ok := config.Colorscheme["selection"]; ok {
|
|
|
|
style = s
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if b.Settings["cursorline"].(bool) && w.active &&
|
|
|
|
!c.HasSelection() && c.Y == bloc.Y {
|
|
|
|
if s, ok := config.Colorscheme["cursor-line"]; ok {
|
|
|
|
fg, _, _ := s.Decompose()
|
|
|
|
style = style.Background(fg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-06 07:36:58 +03:00
|
|
|
for _, m := range b.Messages {
|
|
|
|
if bloc.GreaterEqual(m.Start) && bloc.LessThan(m.End) ||
|
|
|
|
bloc.LessThan(m.End) && bloc.GreaterEqual(m.Start) {
|
|
|
|
style = style.Underline(true)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-14 16:55:15 +03:00
|
|
|
if r == '\t' {
|
2019-12-22 06:08:27 +03:00
|
|
|
indentrunes := []rune(b.Settings["indentchar"].(string))
|
|
|
|
// if empty indentchar settings, use space
|
|
|
|
if indentrunes == nil || len(indentrunes) == 0 {
|
|
|
|
indentrunes = []rune{' '}
|
|
|
|
}
|
2019-07-14 16:55:15 +03:00
|
|
|
|
2019-12-22 06:08:27 +03:00
|
|
|
r = indentrunes[0]
|
|
|
|
if s, ok := config.Colorscheme["indent-char"]; ok && r != ' ' {
|
|
|
|
fg, _, _ := s.Decompose()
|
|
|
|
style = style.Foreground(fg)
|
2019-07-14 16:55:15 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-21 07:45:42 +03:00
|
|
|
if s, ok := config.Colorscheme["color-column"]; ok {
|
|
|
|
if colorcolumn != 0 && vloc.X-w.gutterOffset == colorcolumn {
|
|
|
|
fg, _, _ := s.Decompose()
|
|
|
|
style = style.Background(fg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-05 01:11:09 +03:00
|
|
|
for _, mb := range matchingBraces {
|
|
|
|
if mb.X == bloc.X && mb.Y == bloc.Y {
|
|
|
|
style = style.Underline(true)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-02 06:40:51 +03:00
|
|
|
screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, nil, style)
|
2019-01-14 01:22:11 +03:00
|
|
|
|
|
|
|
if showcursor {
|
|
|
|
for _, c := range cursors {
|
|
|
|
if c.X == bloc.X && c.Y == bloc.Y && !c.HasSelection() {
|
|
|
|
w.showCursor(w.X+vloc.X, w.Y+vloc.Y, c.Num == 0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
vloc.X++
|
|
|
|
}
|
|
|
|
nColsBeforeStart--
|
|
|
|
}
|
|
|
|
|
|
|
|
totalwidth := w.StartCol - nColsBeforeStart
|
|
|
|
for len(line) > 0 {
|
|
|
|
r, size := utf8.DecodeRune(line)
|
|
|
|
curStyle, _ = w.getStyle(curStyle, bloc, r)
|
|
|
|
|
|
|
|
draw(r, curStyle, true)
|
|
|
|
|
|
|
|
width := 0
|
|
|
|
|
|
|
|
char := ' '
|
|
|
|
switch r {
|
|
|
|
case '\t':
|
|
|
|
ts := tabsize - (totalwidth % tabsize)
|
|
|
|
width = ts
|
|
|
|
default:
|
|
|
|
width = runewidth.RuneWidth(r)
|
|
|
|
char = '@'
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw any extra characters either spaces for tabs or @ for incomplete wide runes
|
|
|
|
if width > 1 {
|
|
|
|
for i := 1; i < width; i++ {
|
|
|
|
draw(char, curStyle, false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bloc.X++
|
|
|
|
line = line[size:]
|
|
|
|
|
|
|
|
totalwidth += width
|
|
|
|
|
|
|
|
// If we reach the end of the window then we either stop or we wrap for softwrap
|
2019-08-31 23:44:35 +03:00
|
|
|
if vloc.X >= bufWidth {
|
2019-01-14 01:22:11 +03:00
|
|
|
if !softwrap {
|
|
|
|
break
|
|
|
|
} else {
|
|
|
|
vloc.Y++
|
|
|
|
if vloc.Y >= bufHeight {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
vloc.X = 0
|
|
|
|
// This will draw an empty line number because the current line is wrapped
|
2020-01-06 04:32:29 +03:00
|
|
|
if b.Settings["ruler"].(bool) {
|
|
|
|
w.drawLineNum(lineNumStyle, true, maxLineNumLength, &vloc, &bloc)
|
|
|
|
}
|
2019-01-14 01:22:11 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-21 07:45:42 +03:00
|
|
|
style := config.DefStyle
|
2019-01-14 01:22:11 +03:00
|
|
|
for _, c := range cursors {
|
|
|
|
if b.Settings["cursorline"].(bool) && w.active &&
|
|
|
|
!c.HasSelection() && c.Y == bloc.Y {
|
|
|
|
if s, ok := config.Colorscheme["cursor-line"]; ok {
|
|
|
|
fg, _, _ := s.Decompose()
|
|
|
|
style = style.Background(fg)
|
|
|
|
}
|
2019-01-21 07:45:42 +03:00
|
|
|
}
|
|
|
|
}
|
2019-08-31 23:44:35 +03:00
|
|
|
for i := vloc.X; i < bufWidth; i++ {
|
2019-01-21 07:45:42 +03:00
|
|
|
curStyle := style
|
|
|
|
if s, ok := config.Colorscheme["color-column"]; ok {
|
|
|
|
if colorcolumn != 0 && i-w.gutterOffset == colorcolumn {
|
|
|
|
fg, _, _ := s.Decompose()
|
|
|
|
curStyle = style.Background(fg)
|
2019-01-14 01:22:11 +03:00
|
|
|
}
|
|
|
|
}
|
2020-01-02 06:40:51 +03:00
|
|
|
screen.SetContent(i+w.X, vloc.Y+w.Y, ' ', nil, curStyle)
|
2019-01-14 01:22:11 +03:00
|
|
|
}
|
|
|
|
|
2020-01-05 23:02:52 +03:00
|
|
|
if vloc.X != bufWidth {
|
|
|
|
draw(' ', curStyle, true)
|
|
|
|
}
|
2019-12-22 06:20:49 +03:00
|
|
|
|
2019-01-14 01:22:11 +03:00
|
|
|
bloc.X = w.StartCol
|
|
|
|
bloc.Y++
|
|
|
|
if bloc.Y >= b.LinesNum() {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *BufWindow) displayStatusLine() {
|
2019-01-15 06:16:44 +03:00
|
|
|
_, h := screen.Screen.Size()
|
|
|
|
infoY := h
|
|
|
|
if config.GetGlobalOption("infobar").(bool) {
|
|
|
|
infoY--
|
|
|
|
}
|
|
|
|
|
|
|
|
if w.Buf.Settings["statusline"].(bool) {
|
|
|
|
w.drawStatus = true
|
|
|
|
w.sline.Display()
|
|
|
|
} else if w.Y+w.Height != infoY {
|
|
|
|
w.drawStatus = true
|
|
|
|
for x := w.X; x < w.X+w.Width; x++ {
|
2020-01-02 06:40:51 +03:00
|
|
|
screen.SetContent(x, w.Y+w.Height-1, '-', nil, config.DefStyle.Reverse(true))
|
2019-01-15 06:16:44 +03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
w.drawStatus = false
|
|
|
|
}
|
2019-01-14 01:22:11 +03:00
|
|
|
}
|
|
|
|
|
2019-08-31 23:44:35 +03:00
|
|
|
func (w *BufWindow) displayScrollBar() {
|
|
|
|
if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height {
|
|
|
|
scrollX := w.X + w.Width - 1
|
|
|
|
bufHeight := w.Height
|
|
|
|
if w.drawStatus {
|
|
|
|
bufHeight--
|
|
|
|
}
|
|
|
|
barsize := int(float64(w.Height) / float64(w.Buf.LinesNum()) * float64(w.Height))
|
|
|
|
if barsize < 1 {
|
|
|
|
barsize = 1
|
|
|
|
}
|
2019-12-22 00:19:20 +03:00
|
|
|
barstart := w.Y + int(float64(w.StartLine)/float64(w.Buf.LinesNum())*float64(w.Height))
|
2019-08-31 23:44:35 +03:00
|
|
|
for y := barstart; y < util.Min(barstart+barsize, w.Y+bufHeight); y++ {
|
2020-01-02 06:40:51 +03:00
|
|
|
screen.SetContent(scrollX, y, '|', nil, config.DefStyle.Reverse(true))
|
2019-08-31 23:44:35 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-14 01:22:11 +03:00
|
|
|
// Display displays the buffer and the statusline
|
|
|
|
func (w *BufWindow) Display() {
|
|
|
|
w.displayStatusLine()
|
2019-08-31 23:44:35 +03:00
|
|
|
w.displayScrollBar()
|
2019-01-15 06:16:44 +03:00
|
|
|
w.displayBuffer()
|
2019-01-14 01:22:11 +03:00
|
|
|
}
|