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