sline *StatusLine
- lineHeight []int
- hasCalcHeight bool
- gutterOffset int
- drawStatus bool
+ gutterOffset int
+ drawStatus bool
}
// NewBufWindow creates a new window at a location in the screen with a width and height
w := new(BufWindow)
w.View = new(View)
w.X, w.Y, w.Width, w.Height, w.Buf = x, y, width, height, buf
- w.lineHeight = make([]int, height)
w.active = true
w.sline = NewStatusLine(w)
w.Buf = b
}
-func (v *View) GetView() *View {
- return v
+func (w *BufWindow) GetView() *View {
+ return w.View
}
-func (v *View) SetView(view *View) {
- v = view
+func (w *BufWindow) SetView(view *View) {
+ w.View = view
}
func (w *BufWindow) Resize(width, height int) {
w.Width, w.Height = width, height
- w.lineHeight = make([]int, height)
- w.hasCalcHeight = false
- // This recalculates lineHeight
- w.GetMouseLoc(buffer.Loc{width, height})
+ w.Relocate()
}
func (w *BufWindow) SetActive(b bool) {
func (w *BufWindow) Clear() {
for y := 0; y < w.Height; y++ {
for x := 0; x < w.Width; x++ {
- screen.Screen.SetContent(w.X+x, w.Y+y, ' ', nil, config.DefStyle)
+ screen.SetContent(w.X+x, w.Y+y, ' ', nil, config.DefStyle)
}
}
}
// 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 {
- // b := w.Buf
-
- // TODO: possible non-softwrap optimization
- // if !b.Settings["softwrap"].(bool) {
- // return w.StartLine + w.Height
- // }
-
- prev := 0
- for _, l := range w.lineHeight {
- if l >= prev {
- prev = l
- } else {
- break
+ if !w.Buf.Settings["softwrap"].(bool) {
+ h := w.StartLine + w.Height - 1
+ if w.drawStatus {
+ h--
}
+ return h
}
- return prev
+
+ l := w.LocFromVisual(buffer.Loc{0, w.Y + w.Height})
+
+ return l.Y
}
// Relocate moves the view window so that the cursor is in view
if w.drawStatus {
h--
}
- if b.LinesNum() <= h || !w.hasCalcHeight {
+ if b.LinesNum() <= h {
height = w.Height
}
ret := false
return ret
}
-func (w *BufWindow) GetMouseLoc(svloc buffer.Loc) buffer.Loc {
+// 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 {
b := w.Buf
- // TODO: possible non-softwrap optimization
- // if !b.Settings["softwrap"].(bool) {
- // l := b.LineBytes(svloc.Y)
- // return buffer.Loc{b.GetActiveCursor().GetCharPosInLine(l, svloc.X), svloc.Y}
- // }
-
hasMessage := len(b.Messages) > 0
bufHeight := w.Height
if w.drawStatus {
bufHeight--
}
+ bufWidth := w.Width
+ if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height {
+ bufWidth--
+ }
+
// 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()))
totalwidth += width
// If we reach the end of the window then we either stop or we wrap for softwrap
- if vloc.X >= w.Width {
+ if vloc.X >= bufWidth {
if !softwrap {
break
} else {
}
vloc.X = 0
// This will draw an empty line number because the current line is wrapped
- vloc.X += maxLineNumLength + 1
+ if b.Settings["ruler"].(bool) {
+ vloc.X += maxLineNumLength + 1
+ }
}
}
}
return bloc
}
+ if bloc.Y+1 >= b.LinesNum() || vloc.Y+1 >= bufHeight {
+ return bloc
+ }
+
bloc.X = w.StartCol
bloc.Y++
- if bloc.Y >= b.LinesNum() {
- break
- }
}
return buffer.Loc{}
break
}
}
- screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s)
+ screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s)
vloc.X++
- screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s)
+ screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s)
vloc.X++
}
// Write the spaces before the line number if necessary
for i := 0; i < maxLineNumLength-len(lineNum); i++ {
- screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
+ screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
vloc.X++
}
// Write the actual line number
for _, ch := range lineNum {
if softwrapped {
- screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
+ screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
} else {
- screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ch, nil, lineNumStyle)
+ screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ch, nil, lineNumStyle)
}
vloc.X++
}
// Write the extra space
- screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
+ screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
vloc.X++
}
func (w *BufWindow) showCursor(x, y int, main bool) {
if w.active {
if main {
- screen.Screen.ShowCursor(x, y)
+ screen.ShowCursor(x, y)
} else {
- r, _, _, _ := screen.Screen.GetContent(x, y)
- screen.Screen.SetContent(x, y, r, nil, config.DefStyle.Reverse(true))
+ screen.ShowFakeCursorMulti(x, y)
}
}
}
func (w *BufWindow) displayBuffer() {
b := w.Buf
+ if w.Height <= 0 || w.Width <= 0 {
+ return
+ }
+
hasMessage := len(b.Messages) > 0
bufHeight := w.Height
if w.drawStatus {
bufHeight--
}
- w.hasCalcHeight = true
- start := w.StartLine
+ bufWidth := w.Width
+ if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height {
+ bufWidth--
+ }
+
if b.Settings["syntax"].(bool) && b.SyntaxDef != nil {
- if start > 0 && b.Rehighlight(start-1) {
- b.Highlighter.ReHighlightLine(b, start-1)
- b.SetRehighlight(start-1, false)
+ for _, r := range b.Modifications {
+ final := -1
+ for i := r.X; i <= r.Y; i++ {
+ final = util.Max(b.Highlighter.ReHighlightStates(b, i), final)
+ }
+ b.Highlighter.HighlightMatches(b, r.X, final+1)
}
-
- b.Highlighter.ReHighlightStates(b, start)
-
- b.Highlighter.HighlightMatches(b, w.StartLine, w.StartLine+bufHeight)
+ b.ClearModifications()
}
var matchingBraces []buffer.Loc
r := c.RuneUnder(curX)
rl := c.RuneUnder(curX - 1)
if r == bp[0] || r == bp[1] || rl == bp[0] || rl == bp[1] {
- mb, _ := b.FindMatchingBrace(bp, curLoc)
+ mb, left := b.FindMatchingBrace(bp, curLoc)
matchingBraces = append(matchingBraces, mb)
+ if !left {
+ matchingBraces = append(matchingBraces, curLoc)
+ } else {
+ matchingBraces = append(matchingBraces, curLoc.Move(-1, b))
+ }
}
}
}
}
curNumStyle := config.DefStyle
if style, ok := config.Colorscheme["current-line-number"]; ok {
- curNumStyle = style
+ if !b.Settings["cursorline"].(bool) {
+ curNumStyle = lineNumStyle
+ } else {
+ curNumStyle = style
+ }
}
// We need to know the string length of the largest line number
}
}
+ 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
+ }
+ }
+
if r == '\t' {
- if s, ok := config.Colorscheme["indent-char"]; ok {
- style = s
+ indentrunes := []rune(b.Settings["indentchar"].(string))
+ // if empty indentchar settings, use space
+ if indentrunes == nil || len(indentrunes) == 0 {
+ indentrunes = []rune{' '}
+ }
- indentrunes := []rune(b.Settings["indentchar"].(string))
- // if empty indentchar settings, use space
- if indentrunes == nil || len(indentrunes) == 0 {
- indentrunes = []rune{' '}
- }
- r = indentrunes[0]
+ r = indentrunes[0]
+ if s, ok := config.Colorscheme["indent-char"]; ok && r != ' ' {
+ fg, _, _ := s.Decompose()
+ style = style.Foreground(fg)
}
}
}
}
- screen.Screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, nil, style)
+ screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, nil, style)
if showcursor {
for _, c := range cursors {
nColsBeforeStart--
}
- w.lineHeight[vloc.Y] = bloc.Y
-
totalwidth := w.StartCol - nColsBeforeStart
for len(line) > 0 {
r, size := utf8.DecodeRune(line)
totalwidth += width
// If we reach the end of the window then we either stop or we wrap for softwrap
- if vloc.X >= w.Width {
+ if vloc.X >= bufWidth {
if !softwrap {
break
} else {
break
}
vloc.X = 0
- w.lineHeight[vloc.Y] = bloc.Y
// This will draw an empty line number because the current line is wrapped
- w.drawLineNum(lineNumStyle, true, maxLineNumLength, &vloc, &bloc)
+ if b.Settings["ruler"].(bool) {
+ w.drawLineNum(lineNumStyle, true, maxLineNumLength, &vloc, &bloc)
+ }
}
}
}
}
}
}
- for i := vloc.X; i < w.Width; i++ {
+ for i := vloc.X; i < bufWidth; i++ {
curStyle := style
if s, ok := config.Colorscheme["color-column"]; ok {
if colorcolumn != 0 && i-w.gutterOffset == colorcolumn {
curStyle = style.Background(fg)
}
}
- screen.Screen.SetContent(i+w.X, vloc.Y+w.Y, ' ', nil, curStyle)
+ screen.SetContent(i+w.X, vloc.Y+w.Y, ' ', nil, curStyle)
}
- 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)
- }
+ if vloc.X != bufWidth {
+ draw(' ', curStyle, true)
}
bloc.X = w.StartCol
} else if w.Y+w.Height != infoY {
w.drawStatus = true
for x := w.X; x < w.X+w.Width; x++ {
- screen.Screen.SetContent(x, w.Y+w.Height-1, '-', nil, config.DefStyle.Reverse(true))
+ screen.SetContent(x, w.Y+w.Height-1, '-', nil, config.DefStyle.Reverse(true))
}
} else {
w.drawStatus = false
}
}
+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
+ }
+ barstart := w.Y + int(float64(w.StartLine)/float64(w.Buf.LinesNum())*float64(w.Height))
+ for y := barstart; y < util.Min(barstart+barsize, w.Y+bufHeight); y++ {
+ screen.SetContent(scrollX, y, '|', nil, config.DefStyle.Reverse(true))
+ }
+ }
+}
+
// Display displays the buffer and the statusline
func (w *BufWindow) Display() {
w.displayStatusLine()
+ w.displayScrollBar()
w.displayBuffer()
}