]> git.lizzy.rs Git - micro.git/blob - internal/display/bufwindow.go
6044fd32c8b8a1dfc58b0659b1a56d353284c392
[micro.git] / internal / display / bufwindow.go
1 package display
2
3 import (
4         "strconv"
5
6         runewidth "github.com/mattn/go-runewidth"
7         "github.com/zyedidia/micro/v2/internal/buffer"
8         "github.com/zyedidia/micro/v2/internal/config"
9         "github.com/zyedidia/micro/v2/internal/screen"
10         "github.com/zyedidia/micro/v2/internal/util"
11         "github.com/zyedidia/tcell/v2"
12 )
13
14 // The BufWindow provides a way of displaying a certain section
15 // of a buffer
16 type BufWindow struct {
17         *View
18
19         // Buffer being shown in this window
20         Buf *buffer.Buffer
21
22         active bool
23
24         sline *StatusLine
25
26         bufWidth         int
27         bufHeight        int
28         gutterOffset     int
29         hasMessage       bool
30         maxLineNumLength int
31         drawDivider      bool
32 }
33
34 // NewBufWindow creates a new window at a location in the screen with a width and height
35 func NewBufWindow(x, y, width, height int, buf *buffer.Buffer) *BufWindow {
36         w := new(BufWindow)
37         w.View = new(View)
38         w.X, w.Y, w.Width, w.Height = x, y, width, height
39         w.SetBuffer(buf)
40         w.active = true
41
42         w.sline = NewStatusLine(w)
43
44         return w
45 }
46
47 func (w *BufWindow) SetBuffer(b *buffer.Buffer) {
48         w.Buf = b
49         b.OptionCallback = func(option string, nativeValue interface{}) {
50                 if option == "softwrap" {
51                         if nativeValue.(bool) {
52                                 w.StartCol = 0
53                         } else {
54                                 w.StartLine.Row = 0
55                         }
56                         w.Relocate()
57
58                         for _, c := range w.Buf.GetCursors() {
59                                 c.LastVisualX = c.GetVisualX()
60                         }
61                 }
62         }
63         b.GetVisualX = func(loc buffer.Loc) int {
64                 return w.VLocFromLoc(loc).VisualX
65         }
66 }
67
68 func (w *BufWindow) GetView() *View {
69         return w.View
70 }
71
72 func (w *BufWindow) SetView(view *View) {
73         w.View = view
74 }
75
76 func (w *BufWindow) Resize(width, height int) {
77         w.Width, w.Height = width, height
78         w.updateDisplayInfo()
79
80         w.Relocate()
81
82         if w.Buf.Settings["softwrap"].(bool) {
83                 for _, c := range w.Buf.GetCursors() {
84                         c.LastVisualX = c.GetVisualX()
85                 }
86         }
87 }
88
89 func (w *BufWindow) SetActive(b bool) {
90         w.active = b
91 }
92
93 func (w *BufWindow) IsActive() bool {
94         return w.active
95 }
96
97 // BufView returns the width, height and x,y location of the actual buffer.
98 // It is not exactly the same as the whole window which also contains gutter,
99 // ruler, scrollbar and statusline.
100 func (w *BufWindow) BufView() View {
101         return View{
102                 X:         w.X + w.gutterOffset,
103                 Y:         w.Y,
104                 Width:     w.bufWidth,
105                 Height:    w.bufHeight,
106                 StartLine: w.StartLine,
107                 StartCol:  w.StartCol,
108         }
109 }
110
111 func (w *BufWindow) updateDisplayInfo() {
112         b := w.Buf
113
114         w.drawDivider = false
115         if !b.Settings["statusline"].(bool) {
116                 _, h := screen.Screen.Size()
117                 infoY := h
118                 if config.GetGlobalOption("infobar").(bool) {
119                         infoY--
120                 }
121                 if w.Y+w.Height != infoY {
122                         w.drawDivider = true
123                 }
124         }
125
126         w.bufHeight = w.Height
127         if b.Settings["statusline"].(bool) || w.drawDivider {
128                 w.bufHeight--
129         }
130
131         w.hasMessage = len(b.Messages) > 0
132
133         // We need to know the string length of the largest line number
134         // so we can pad appropriately when displaying line numbers
135         w.maxLineNumLength = len(strconv.Itoa(b.LinesNum()))
136
137         w.gutterOffset = 0
138         if w.hasMessage {
139                 w.gutterOffset += 2
140         }
141         if b.Settings["diffgutter"].(bool) {
142                 w.gutterOffset++
143         }
144         if b.Settings["ruler"].(bool) {
145                 w.gutterOffset += w.maxLineNumLength + 1
146         }
147
148         w.bufWidth = w.Width - w.gutterOffset
149         if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height {
150                 w.bufWidth--
151         }
152 }
153
154 func (w *BufWindow) getStartInfo(n, lineN int) ([]byte, int, int, *tcell.Style) {
155         tabsize := util.IntOpt(w.Buf.Settings["tabsize"])
156         width := 0
157         bloc := buffer.Loc{0, lineN}
158         b := w.Buf.LineBytes(lineN)
159         curStyle := config.DefStyle
160         var s *tcell.Style
161         for len(b) > 0 {
162                 r, _, size := util.DecodeCharacter(b)
163
164                 curStyle, found := w.getStyle(curStyle, bloc)
165                 if found {
166                         s = &curStyle
167                 }
168
169                 w := 0
170                 switch r {
171                 case '\t':
172                         ts := tabsize - (width % tabsize)
173                         w = ts
174                 default:
175                         w = runewidth.RuneWidth(r)
176                 }
177                 if width+w > n {
178                         return b, n - width, bloc.X, s
179                 }
180                 width += w
181                 b = b[size:]
182                 bloc.X++
183         }
184         return b, n - width, bloc.X, s
185 }
186
187 // Clear resets all cells in this window to the default style
188 func (w *BufWindow) Clear() {
189         for y := 0; y < w.Height; y++ {
190                 for x := 0; x < w.Width; x++ {
191                         screen.SetContent(w.X+x, w.Y+y, ' ', nil, config.DefStyle)
192                 }
193         }
194 }
195
196 // Relocate moves the view window so that the cursor is in view
197 // This is useful if the user has scrolled far away, and then starts typing
198 // Returns true if the window location is moved
199 func (w *BufWindow) Relocate() bool {
200         b := w.Buf
201         height := w.bufHeight
202         ret := false
203         activeC := w.Buf.GetActiveCursor()
204         scrollmargin := int(b.Settings["scrollmargin"].(float64))
205
206         c := w.SLocFromLoc(activeC.Loc)
207         bStart := SLoc{0, 0}
208         bEnd := w.SLocFromLoc(b.End())
209
210         if c.LessThan(w.Scroll(w.StartLine, scrollmargin)) && c.GreaterThan(w.Scroll(bStart, scrollmargin-1)) {
211                 w.StartLine = w.Scroll(c, -scrollmargin)
212                 ret = true
213         } else if c.LessThan(w.StartLine) {
214                 w.StartLine = c
215                 ret = true
216         }
217         if c.GreaterThan(w.Scroll(w.StartLine, height-1-scrollmargin)) && c.LessThan(w.Scroll(bEnd, -scrollmargin+1)) {
218                 w.StartLine = w.Scroll(c, -height+1+scrollmargin)
219                 ret = true
220         } else if c.GreaterThan(w.Scroll(bEnd, -scrollmargin)) && c.GreaterThan(w.Scroll(w.StartLine, height-1)) {
221                 w.StartLine = w.Scroll(bEnd, -height+1)
222                 ret = true
223         }
224
225         // horizontal relocation (scrolling)
226         if !b.Settings["softwrap"].(bool) {
227                 cx := activeC.GetVisualX()
228                 rw := runewidth.RuneWidth(activeC.RuneUnder(activeC.X))
229                 if rw == 0 {
230                         rw = 1 // tab or newline
231                 }
232
233                 if cx < w.StartCol {
234                         w.StartCol = cx
235                         ret = true
236                 }
237                 if cx+w.gutterOffset+rw > w.StartCol+w.Width {
238                         w.StartCol = cx - w.Width + w.gutterOffset + rw
239                         ret = true
240                 }
241         }
242         return ret
243 }
244
245 // LocFromVisual takes a visual location (x and y position) and returns the
246 // position in the buffer corresponding to the visual location
247 // If the requested position does not correspond to a buffer location it returns
248 // the nearest position
249 func (w *BufWindow) LocFromVisual(svloc buffer.Loc) buffer.Loc {
250         vx := svloc.X - w.X - w.gutterOffset
251         if vx < 0 {
252                 vx = 0
253         }
254         vloc := VLoc{
255                 SLoc:    w.Scroll(w.StartLine, svloc.Y-w.Y),
256                 VisualX: vx + w.StartCol,
257         }
258         return w.LocFromVLoc(vloc)
259 }
260
261 func (w *BufWindow) drawGutter(vloc *buffer.Loc, bloc *buffer.Loc) {
262         char := ' '
263         s := config.DefStyle
264         for _, m := range w.Buf.Messages {
265                 if m.Start.Y == bloc.Y || m.End.Y == bloc.Y {
266                         s = m.Style()
267                         char = '>'
268                         break
269                 }
270         }
271         screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s)
272         vloc.X++
273         screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, char, nil, s)
274         vloc.X++
275 }
276
277 func (w *BufWindow) drawDiffGutter(backgroundStyle tcell.Style, softwrapped bool, vloc *buffer.Loc, bloc *buffer.Loc) {
278         symbol := ' '
279         styleName := ""
280
281         switch w.Buf.DiffStatus(bloc.Y) {
282         case buffer.DSAdded:
283                 symbol = '\u258C' // Left half block
284                 styleName = "diff-added"
285         case buffer.DSModified:
286                 symbol = '\u258C' // Left half block
287                 styleName = "diff-modified"
288         case buffer.DSDeletedAbove:
289                 if !softwrapped {
290                         symbol = '\u2594' // Upper one eighth block
291                         styleName = "diff-deleted"
292                 }
293         }
294
295         style := backgroundStyle
296         if s, ok := config.Colorscheme[styleName]; ok {
297                 foreground, _, _ := s.Decompose()
298                 style = style.Foreground(foreground)
299         }
300
301         screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, symbol, nil, style)
302         vloc.X++
303 }
304
305 func (w *BufWindow) drawLineNum(lineNumStyle tcell.Style, softwrapped bool, vloc *buffer.Loc, bloc *buffer.Loc) {
306         cursorLine := w.Buf.GetActiveCursor().Loc.Y
307         var lineInt int
308         if w.Buf.Settings["relativeruler"] == false || cursorLine == bloc.Y {
309                 lineInt = bloc.Y + 1
310         } else {
311                 lineInt = bloc.Y - cursorLine
312         }
313         lineNum := strconv.Itoa(util.Abs(lineInt))
314
315         // Write the spaces before the line number if necessary
316         for i := 0; i < w.maxLineNumLength-len(lineNum); i++ {
317                 screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
318                 vloc.X++
319         }
320         // Write the actual line number
321         for _, ch := range lineNum {
322                 if softwrapped {
323                         screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
324                 } else {
325                         screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ch, nil, lineNumStyle)
326                 }
327                 vloc.X++
328         }
329
330         // Write the extra space
331         screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, ' ', nil, lineNumStyle)
332         vloc.X++
333 }
334
335 // getStyle returns the highlight style for the given character position
336 // If there is no change to the current highlight style it just returns that
337 func (w *BufWindow) getStyle(style tcell.Style, bloc buffer.Loc) (tcell.Style, bool) {
338         if group, ok := w.Buf.Match(bloc.Y)[bloc.X]; ok {
339                 s := config.GetColor(group.String())
340                 return s, true
341         }
342         return style, false
343 }
344
345 func (w *BufWindow) showCursor(x, y int, main bool) {
346         if w.active {
347                 if main {
348                         screen.ShowCursor(x, y)
349                 } else {
350                         screen.ShowFakeCursorMulti(x, y)
351                 }
352         }
353 }
354
355 // displayBuffer draws the buffer being shown in this window on the screen.Screen
356 func (w *BufWindow) displayBuffer() {
357         b := w.Buf
358
359         if w.Height <= 0 || w.Width <= 0 {
360                 return
361         }
362
363         maxWidth := w.gutterOffset + w.bufWidth
364
365         if b.ModifiedThisFrame {
366                 if b.Settings["diffgutter"].(bool) {
367                         b.UpdateDiff(func(synchronous bool) {
368                                 // If the diff was updated asynchronously, the outer call to
369                                 // displayBuffer might already be completed and we need to
370                                 // schedule a redraw in order to display the new diff.
371                                 // Note that this cannot lead to an infinite recursion
372                                 // because the modifications were cleared above so there won't
373                                 // be another call to UpdateDiff when displayBuffer is called
374                                 // during the redraw.
375                                 if !synchronous {
376                                         screen.Redraw()
377                                 }
378                         })
379                 }
380                 b.ModifiedThisFrame = false
381         }
382
383         var matchingBraces []buffer.Loc
384         // bracePairs is defined in buffer.go
385         if b.Settings["matchbrace"].(bool) {
386                 for _, bp := range buffer.BracePairs {
387                         for _, c := range b.GetCursors() {
388                                 if c.HasSelection() {
389                                         continue
390                                 }
391                                 curX := c.X
392                                 curLoc := c.Loc
393
394                                 r := c.RuneUnder(curX)
395                                 rl := c.RuneUnder(curX - 1)
396                                 if r == bp[0] || r == bp[1] || rl == bp[0] || rl == bp[1] {
397                                         mb, left, found := b.FindMatchingBrace(bp, curLoc)
398                                         if found {
399                                                 matchingBraces = append(matchingBraces, mb)
400                                                 if !left {
401                                                         matchingBraces = append(matchingBraces, curLoc)
402                                                 } else {
403                                                         matchingBraces = append(matchingBraces, curLoc.Move(-1, b))
404                                                 }
405                                         }
406                                 }
407                         }
408                 }
409         }
410
411         lineNumStyle := config.DefStyle
412         if style, ok := config.Colorscheme["line-number"]; ok {
413                 lineNumStyle = style
414         }
415         curNumStyle := config.DefStyle
416         if style, ok := config.Colorscheme["current-line-number"]; ok {
417                 if !b.Settings["cursorline"].(bool) {
418                         curNumStyle = lineNumStyle
419                 } else {
420                         curNumStyle = style
421                 }
422         }
423
424         softwrap := b.Settings["softwrap"].(bool)
425         wordwrap := softwrap && b.Settings["wordwrap"].(bool)
426
427         tabsize := util.IntOpt(b.Settings["tabsize"])
428         colorcolumn := util.IntOpt(b.Settings["colorcolumn"])
429
430         // this represents the current draw position
431         // within the current window
432         vloc := buffer.Loc{X: 0, Y: 0}
433         if softwrap {
434                 // the start line may be partially out of the current window
435                 vloc.Y = -w.StartLine.Row
436         }
437
438         // this represents the current draw position in the buffer (char positions)
439         bloc := buffer.Loc{X: -1, Y: w.StartLine.Line}
440
441         cursors := b.GetCursors()
442
443         curStyle := config.DefStyle
444         for ; vloc.Y < w.bufHeight; vloc.Y++ {
445                 vloc.X = 0
446
447                 currentLine := false
448                 for _, c := range cursors {
449                         if bloc.Y == c.Y && w.active {
450                                 currentLine = true
451                                 break
452                         }
453                 }
454
455                 s := lineNumStyle
456                 if currentLine {
457                         s = curNumStyle
458                 }
459
460                 if vloc.Y >= 0 {
461                         if w.hasMessage {
462                                 w.drawGutter(&vloc, &bloc)
463                         }
464
465                         if b.Settings["diffgutter"].(bool) {
466                                 w.drawDiffGutter(s, false, &vloc, &bloc)
467                         }
468
469                         if b.Settings["ruler"].(bool) {
470                                 w.drawLineNum(s, false, &vloc, &bloc)
471                         }
472                 } else {
473                         vloc.X = w.gutterOffset
474                 }
475
476                 line, nColsBeforeStart, bslice, startStyle := w.getStartInfo(w.StartCol, bloc.Y)
477                 if startStyle != nil {
478                         curStyle = *startStyle
479                 }
480                 bloc.X = bslice
481
482                 draw := func(r rune, combc []rune, style tcell.Style, highlight bool, showcursor bool) {
483                         if nColsBeforeStart <= 0 && vloc.Y >= 0 {
484                                 if highlight {
485                                         _, origBg, _ := style.Decompose()
486                                         _, defBg, _ := config.DefStyle.Decompose()
487
488                                         // syntax highlighting with non-default background takes precedence
489                                         // over cursor-line and color-column
490                                         dontOverrideBackground := origBg != defBg
491
492                                         for _, c := range cursors {
493                                                 if c.HasSelection() &&
494                                                         (bloc.GreaterEqual(c.CurSelection[0]) && bloc.LessThan(c.CurSelection[1]) ||
495                                                                 bloc.LessThan(c.CurSelection[0]) && bloc.GreaterEqual(c.CurSelection[1])) {
496                                                         // The current character is selected
497                                                         style = config.DefStyle.Reverse(true)
498
499                                                         if s, ok := config.Colorscheme["selection"]; ok {
500                                                                 style = s
501                                                         }
502                                                 }
503
504                                                 if b.Settings["cursorline"].(bool) && w.active && !dontOverrideBackground &&
505                                                         !c.HasSelection() && c.Y == bloc.Y {
506                                                         if s, ok := config.Colorscheme["cursor-line"]; ok {
507                                                                 fg, _, _ := s.Decompose()
508                                                                 style = style.Background(fg)
509                                                         }
510                                                 }
511                                         }
512
513                                         for _, m := range b.Messages {
514                                                 if bloc.GreaterEqual(m.Start) && bloc.LessThan(m.End) ||
515                                                         bloc.LessThan(m.End) && bloc.GreaterEqual(m.Start) {
516                                                         style = style.Underline(true)
517                                                         break
518                                                 }
519                                         }
520
521                                         if r == '\t' {
522                                                 indentrunes := []rune(b.Settings["indentchar"].(string))
523                                                 // if empty indentchar settings, use space
524                                                 if len(indentrunes) == 0 {
525                                                         indentrunes = []rune{' '}
526                                                 }
527
528                                                 r = indentrunes[0]
529                                                 if s, ok := config.Colorscheme["indent-char"]; ok && r != ' ' {
530                                                         fg, _, _ := s.Decompose()
531                                                         style = style.Foreground(fg)
532                                                 }
533                                         }
534
535                                         if s, ok := config.Colorscheme["color-column"]; ok {
536                                                 if colorcolumn != 0 && vloc.X-w.gutterOffset+w.StartCol == colorcolumn && !dontOverrideBackground {
537                                                         fg, _, _ := s.Decompose()
538                                                         style = style.Background(fg)
539                                                 }
540                                         }
541
542                                         for _, mb := range matchingBraces {
543                                                 if mb.X == bloc.X && mb.Y == bloc.Y {
544                                                         style = style.Underline(true)
545                                                 }
546                                         }
547                                 }
548
549                                 screen.SetContent(w.X+vloc.X, w.Y+vloc.Y, r, combc, style)
550
551                                 if showcursor {
552                                         for _, c := range cursors {
553                                                 if c.X == bloc.X && c.Y == bloc.Y && !c.HasSelection() {
554                                                         w.showCursor(w.X+vloc.X, w.Y+vloc.Y, c.Num == 0)
555                                                 }
556                                         }
557                                 }
558                         }
559                         if nColsBeforeStart <= 0 {
560                                 vloc.X++
561                         }
562                         nColsBeforeStart--
563                 }
564
565                 wrap := func() {
566                         vloc.X = 0
567                         if w.hasMessage {
568                                 w.drawGutter(&vloc, &bloc)
569                         }
570                         if b.Settings["diffgutter"].(bool) {
571                                 w.drawDiffGutter(lineNumStyle, true, &vloc, &bloc)
572                         }
573
574                         // This will draw an empty line number because the current line is wrapped
575                         if b.Settings["ruler"].(bool) {
576                                 w.drawLineNum(lineNumStyle, true, &vloc, &bloc)
577                         }
578                 }
579
580                 type glyph struct {
581                         r     rune
582                         combc []rune
583                         style tcell.Style
584                         width int
585                 }
586
587                 var word []glyph
588                 if wordwrap {
589                         word = make([]glyph, 0, w.bufWidth)
590                 } else {
591                         word = make([]glyph, 0, 1)
592                 }
593                 wordwidth := 0
594
595                 totalwidth := w.StartCol - nColsBeforeStart
596                 for len(line) > 0 {
597                         r, combc, size := util.DecodeCharacter(line)
598                         line = line[size:]
599
600                         loc := buffer.Loc{X: bloc.X + len(word), Y: bloc.Y}
601                         curStyle, _ = w.getStyle(curStyle, loc)
602
603                         width := 0
604
605                         switch r {
606                         case '\t':
607                                 ts := tabsize - (totalwidth % tabsize)
608                                 width = util.Min(ts, maxWidth-vloc.X)
609                                 totalwidth += ts
610                         default:
611                                 width = runewidth.RuneWidth(r)
612                                 totalwidth += width
613                         }
614
615                         word = append(word, glyph{r, combc, curStyle, width})
616                         wordwidth += width
617
618                         // Collect a complete word to know its width.
619                         // If wordwrap is off, every single character is a complete "word".
620                         if wordwrap {
621                                 if !util.IsWhitespace(r) && len(line) > 0 && wordwidth < w.bufWidth {
622                                         continue
623                                 }
624                         }
625
626                         // If a word (or just a wide rune) does not fit in the window
627                         if vloc.X+wordwidth > maxWidth && vloc.X > w.gutterOffset {
628                                 for vloc.X < maxWidth {
629                                         draw(' ', nil, config.DefStyle, false, false)
630                                 }
631
632                                 // We either stop or we wrap to draw the word in the next line
633                                 if !softwrap {
634                                         break
635                                 } else {
636                                         vloc.Y++
637                                         if vloc.Y >= w.bufHeight {
638                                                 break
639                                         }
640                                         wrap()
641                                 }
642                         }
643
644                         for _, r := range word {
645                                 draw(r.r, r.combc, r.style, true, true)
646
647                                 // Draw any extra characters either spaces for tabs or @ for incomplete wide runes
648                                 if r.width > 1 {
649                                         char := ' '
650                                         if r.r != '\t' {
651                                                 char = '@'
652                                         }
653
654                                         for i := 1; i < r.width; i++ {
655                                                 draw(char, nil, r.style, true, false)
656                                         }
657                                 }
658                                 bloc.X++
659                         }
660
661                         word = word[:0]
662                         wordwidth = 0
663
664                         // If we reach the end of the window then we either stop or we wrap for softwrap
665                         if vloc.X >= maxWidth {
666                                 if !softwrap {
667                                         break
668                                 } else {
669                                         vloc.Y++
670                                         if vloc.Y >= w.bufHeight {
671                                                 break
672                                         }
673                                         wrap()
674                                 }
675                         }
676                 }
677
678                 style := config.DefStyle
679                 for _, c := range cursors {
680                         if b.Settings["cursorline"].(bool) && w.active &&
681                                 !c.HasSelection() && c.Y == bloc.Y {
682                                 if s, ok := config.Colorscheme["cursor-line"]; ok {
683                                         fg, _, _ := s.Decompose()
684                                         style = style.Background(fg)
685                                 }
686                         }
687                 }
688                 for i := vloc.X; i < maxWidth; i++ {
689                         curStyle := style
690                         if s, ok := config.Colorscheme["color-column"]; ok {
691                                 if colorcolumn != 0 && i-w.gutterOffset+w.StartCol == colorcolumn {
692                                         fg, _, _ := s.Decompose()
693                                         curStyle = style.Background(fg)
694                                 }
695                         }
696                         screen.SetContent(i+w.X, vloc.Y+w.Y, ' ', nil, curStyle)
697                 }
698
699                 if vloc.X != maxWidth {
700                         // Display newline within a selection
701                         draw(' ', nil, config.DefStyle, true, true)
702                 }
703
704                 bloc.X = w.StartCol
705                 bloc.Y++
706                 if bloc.Y >= b.LinesNum() {
707                         break
708                 }
709         }
710 }
711
712 func (w *BufWindow) displayStatusLine() {
713         if w.Buf.Settings["statusline"].(bool) {
714                 w.sline.Display()
715         } else if w.drawDivider {
716                 divchars := config.GetGlobalOption("divchars").(string)
717                 if util.CharacterCountInString(divchars) != 2 {
718                         divchars = "|-"
719                 }
720
721                 _, _, size := util.DecodeCharacterInString(divchars)
722                 divchar, combc, _ := util.DecodeCharacterInString(divchars[size:])
723
724                 dividerStyle := config.DefStyle
725                 if style, ok := config.Colorscheme["divider"]; ok {
726                         dividerStyle = style
727                 }
728
729                 divreverse := config.GetGlobalOption("divreverse").(bool)
730                 if divreverse {
731                         dividerStyle = dividerStyle.Reverse(true)
732                 }
733
734                 for x := w.X; x < w.X+w.Width; x++ {
735                         screen.SetContent(x, w.Y+w.Height-1, divchar, combc, dividerStyle)
736                 }
737         }
738 }
739
740 func (w *BufWindow) displayScrollBar() {
741         if w.Buf.Settings["scrollbar"].(bool) && w.Buf.LinesNum() > w.Height {
742                 scrollX := w.X + w.Width - 1
743                 barsize := int(float64(w.Height) / float64(w.Buf.LinesNum()) * float64(w.Height))
744                 if barsize < 1 {
745                         barsize = 1
746                 }
747                 barstart := w.Y + int(float64(w.StartLine.Line)/float64(w.Buf.LinesNum())*float64(w.Height))
748
749                 scrollBarStyle := config.DefStyle.Reverse(true)
750                 if style, ok := config.Colorscheme["scrollbar"]; ok {
751                         scrollBarStyle = style
752                 }
753
754                 for y := barstart; y < util.Min(barstart+barsize, w.Y+w.bufHeight); y++ {
755                         screen.SetContent(scrollX, y, '|', nil, scrollBarStyle)
756                 }
757         }
758 }
759
760 // Display displays the buffer and the statusline
761 func (w *BufWindow) Display() {
762         w.updateDisplayInfo()
763
764         w.displayStatusLine()
765         w.displayScrollBar()
766         w.displayBuffer()
767 }