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