]> git.lizzy.rs Git - micro.git/blob - cmd/micro/view.go
Create ~/.micro if it does not exist
[micro.git] / cmd / micro / view.go
1 package main
2
3 import (
4         "github.com/atotto/clipboard"
5         "github.com/gdamore/tcell"
6         "io/ioutil"
7         "strconv"
8         "strings"
9         "time"
10 )
11
12 // The View struct stores information about a view into a buffer.
13 // It has a stores information about the cursor, and the viewport
14 // that the user sees the buffer from.
15 type View struct {
16         cursor Cursor
17
18         // The topmost line, used for vertical scrolling
19         topline int
20         // The leftmost column, used for horizontal scrolling
21         leftCol int
22
23         // Percentage of the terminal window that this view takes up (from 0 to 100)
24         widthPercent  int
25         heightPercent int
26
27         // Actual with and height
28         width  int
29         height int
30
31         // How much to offset because of line numbers
32         lineNumOffset int
33
34         // The eventhandler for undo/redo
35         eh *EventHandler
36
37         // The buffer
38         buf *Buffer
39         // The statusline
40         sline Statusline
41
42         // Since tcell doesn't differentiate between a mouse release event
43         // and a mouse move event with no keys pressed, we need to keep
44         // track of whether or not the mouse was pressed (or not released) last event to determine
45         // mouse release events
46         mouseReleased bool
47
48         // This stores when the last click was
49         // This is useful for detecting double and triple clicks
50         lastClickTime time.Time
51
52         // Was the last mouse event actually a double click?
53         // Useful for detecting triple clicks -- if a double click is detected
54         // but the last mouse event was actually a double click, it's a triple click
55         doubleClick bool
56         // Same here, just to keep track for mouse move events
57         tripleClick bool
58
59         // Syntax highlighting matches
60         matches SyntaxMatches
61         // The matches from the last frame
62         lastMatches SyntaxMatches
63
64         // This is the range of lines that should have their syntax highlighting updated
65         updateLines [2]int
66 }
67
68 // NewView returns a new fullscreen view
69 func NewView(buf *Buffer) *View {
70         return NewViewWidthHeight(buf, 100, 100)
71 }
72
73 // NewViewWidthHeight returns a new view with the specified width and height percentages
74 // Note that w and h are percentages not actual values
75 func NewViewWidthHeight(buf *Buffer, w, h int) *View {
76         v := new(View)
77
78         v.buf = buf
79
80         v.widthPercent = w
81         v.heightPercent = h
82         v.Resize(screen.Size())
83
84         v.topline = 0
85         // Put the cursor at the first spot
86         v.cursor = Cursor{
87                 x: 0,
88                 y: 0,
89                 v: v,
90         }
91         v.cursor.ResetSelection()
92
93         v.eh = NewEventHandler(v)
94
95         v.sline = Statusline{
96                 view: v,
97         }
98
99         // Update the syntax highlighting for the entire buffer at the start
100         v.UpdateLines(v.topline, v.topline+v.height)
101         v.matches = Match(v)
102
103         // Set mouseReleased to true because we assume the mouse is not being pressed when
104         // the editor is opened
105         v.mouseReleased = true
106         v.lastClickTime = time.Time{}
107
108         return v
109 }
110
111 // UpdateLines sets the values for v.updateLines
112 func (v *View) UpdateLines(start, end int) {
113         v.updateLines[0] = start
114         v.updateLines[1] = end + 1
115 }
116
117 // Resize recalculates the actual width and height of the view from the width and height
118 // percentages
119 // This is usually called when the window is resized, or when a split has been added and
120 // the percentages have changed
121 func (v *View) Resize(w, h int) {
122         // Always include 1 line for the command line at the bottom
123         h--
124         v.width = int(float32(w) * float32(v.widthPercent) / 100)
125         // We subtract 1 for the statusline
126         v.height = int(float32(h)*float32(v.heightPercent)/100) - 1
127 }
128
129 // ScrollUp scrolls the view up n lines (if possible)
130 func (v *View) ScrollUp(n int) {
131         // Try to scroll by n but if it would overflow, scroll by 1
132         if v.topline-n >= 0 {
133                 v.topline -= n
134         } else if v.topline > 0 {
135                 v.topline--
136         }
137 }
138
139 // ScrollDown scrolls the view down n lines (if possible)
140 func (v *View) ScrollDown(n int) {
141         // Try to scroll by n but if it would overflow, scroll by 1
142         if v.topline+n <= len(v.buf.lines)-v.height {
143                 v.topline += n
144         } else if v.topline < len(v.buf.lines)-v.height {
145                 v.topline++
146         }
147 }
148
149 // PageUp scrolls the view up a page
150 func (v *View) PageUp() {
151         if v.topline > v.height {
152                 v.ScrollUp(v.height)
153         } else {
154                 v.topline = 0
155         }
156 }
157
158 // PageDown scrolls the view down a page
159 func (v *View) PageDown() {
160         if len(v.buf.lines)-(v.topline+v.height) > v.height {
161                 v.ScrollDown(v.height)
162         } else {
163                 if len(v.buf.lines) >= v.height {
164                         v.topline = len(v.buf.lines) - v.height
165                 }
166         }
167 }
168
169 // HalfPageUp scrolls the view up half a page
170 func (v *View) HalfPageUp() {
171         if v.topline > v.height/2 {
172                 v.ScrollUp(v.height / 2)
173         } else {
174                 v.topline = 0
175         }
176 }
177
178 // HalfPageDown scrolls the view down half a page
179 func (v *View) HalfPageDown() {
180         if len(v.buf.lines)-(v.topline+v.height) > v.height/2 {
181                 v.ScrollDown(v.height / 2)
182         } else {
183                 if len(v.buf.lines) >= v.height {
184                         v.topline = len(v.buf.lines) - v.height
185                 }
186         }
187 }
188
189 // CanClose returns whether or not the view can be closed
190 // If there are unsaved changes, the user will be asked if the view can be closed
191 // causing them to lose the unsaved changes
192 // The message is what to print after saying "You have unsaved changes. "
193 func (v *View) CanClose(msg string) bool {
194         if v.buf.IsDirty() {
195                 quit, canceled := messenger.Prompt("You have unsaved changes. " + msg)
196                 if !canceled {
197                         if strings.ToLower(quit) == "yes" || strings.ToLower(quit) == "y" {
198                                 return true
199                         }
200                 }
201         } else {
202                 return true
203         }
204         return false
205 }
206
207 // Save the buffer to disk
208 func (v *View) Save() {
209         // If this is an empty buffer, ask for a filename
210         if v.buf.path == "" {
211                 filename, canceled := messenger.Prompt("Filename: ")
212                 if !canceled {
213                         v.buf.path = filename
214                         v.buf.name = filename
215                 } else {
216                         return
217                 }
218         }
219         err := v.buf.Save()
220         if err != nil {
221                 messenger.Error(err.Error())
222         } else {
223                 messenger.Message("Saved " + v.buf.path)
224         }
225 }
226
227 // Copy the selection to the system clipboard
228 func (v *View) Copy() {
229         if v.cursor.HasSelection() {
230                 if !clipboard.Unsupported {
231                         clipboard.WriteAll(v.cursor.GetSelection())
232                 } else {
233                         messenger.Error("Clipboard is not supported on your system")
234                 }
235         }
236 }
237
238 // Cut the selection to the system clipboard
239 func (v *View) Cut() {
240         if v.cursor.HasSelection() {
241                 if !clipboard.Unsupported {
242                         clipboard.WriteAll(v.cursor.GetSelection())
243                         v.cursor.DeleteSelection()
244                         v.cursor.ResetSelection()
245                 } else {
246                         messenger.Error("Clipboard is not supported on your system")
247                 }
248         }
249 }
250
251 // Paste whatever is in the system clipboard into the buffer
252 // Delete and paste if the user has a selection
253 func (v *View) Paste() {
254         if !clipboard.Unsupported {
255                 if v.cursor.HasSelection() {
256                         v.cursor.DeleteSelection()
257                         v.cursor.ResetSelection()
258                 }
259                 clip, _ := clipboard.ReadAll()
260                 v.eh.Insert(v.cursor.Loc(), clip)
261                 v.cursor.SetLoc(v.cursor.Loc() + Count(clip))
262         } else {
263                 messenger.Error("Clipboard is not supported on your system")
264         }
265 }
266
267 // SelectAll selects the entire buffer
268 func (v *View) SelectAll() {
269         v.cursor.curSelection[1] = 0
270         v.cursor.curSelection[0] = v.buf.Len()
271         // Put the cursor at the beginning
272         v.cursor.x = 0
273         v.cursor.y = 0
274 }
275
276 // OpenFile opens a new file in the current view
277 // It makes sure that the current buffer can be closed first (unsaved changes)
278 func (v *View) OpenFile() {
279         if v.CanClose("Continue? ") {
280                 filename, canceled := messenger.Prompt("File to open: ")
281                 if canceled {
282                         return
283                 }
284                 file, err := ioutil.ReadFile(filename)
285
286                 if err != nil {
287                         messenger.Error(err.Error())
288                         return
289                 }
290                 v.buf = NewBuffer(string(file), filename)
291         }
292 }
293
294 // Relocate moves the view window so that the cursor is in view
295 // This is useful if the user has scrolled far away, and then starts typing
296 func (v *View) Relocate() bool {
297         ret := false
298         cy := v.cursor.y
299         if cy < v.topline {
300                 v.topline = cy
301                 ret = true
302         }
303         if cy > v.topline+v.height-1 {
304                 v.topline = cy - v.height + 1
305                 ret = true
306         }
307
308         cx := v.cursor.GetVisualX()
309         if cx < v.leftCol {
310                 v.leftCol = cx
311                 ret = true
312         }
313         if cx+v.lineNumOffset+1 > v.leftCol+v.width {
314                 v.leftCol = cx - v.width + v.lineNumOffset + 1
315                 ret = true
316         }
317         return ret
318 }
319
320 // MoveToMouseClick moves the cursor to location x, y assuming x, y were given
321 // by a mouse click
322 func (v *View) MoveToMouseClick(x, y int) {
323         if y-v.topline > v.height-1 {
324                 v.ScrollDown(1)
325                 y = v.height + v.topline - 1
326         }
327         if y >= len(v.buf.lines) {
328                 y = len(v.buf.lines) - 1
329         }
330         if x < 0 {
331                 x = 0
332         }
333
334         x = v.cursor.GetCharPosInLine(y, x)
335         if x > Count(v.buf.lines[y]) {
336                 x = Count(v.buf.lines[y])
337         }
338         v.cursor.x = x
339         v.cursor.y = y
340         v.cursor.lastVisualX = v.cursor.GetVisualX()
341 }
342
343 // HandleEvent handles an event passed by the main loop
344 func (v *View) HandleEvent(event tcell.Event) {
345         // This bool determines whether the view is relocated at the end of the function
346         // By default it's true because most events should cause a relocate
347         relocate := true
348
349         // By default we don't update and syntax highlighting
350         v.UpdateLines(-2, 0)
351         switch e := event.(type) {
352         case *tcell.EventResize:
353                 // Window resized
354                 v.Resize(e.Size())
355         case *tcell.EventKey:
356                 switch e.Key() {
357                 case tcell.KeyUp:
358                         // Cursor up
359                         v.cursor.ResetSelection()
360                         v.cursor.Up()
361                 case tcell.KeyDown:
362                         // Cursor down
363                         v.cursor.ResetSelection()
364                         v.cursor.Down()
365                 case tcell.KeyLeft:
366                         // Cursor left
367                         v.cursor.ResetSelection()
368                         v.cursor.Left()
369                 case tcell.KeyRight:
370                         // Cursor right
371                         v.cursor.ResetSelection()
372                         v.cursor.Right()
373                 case tcell.KeyEnter:
374                         // Insert a newline
375                         if v.cursor.HasSelection() {
376                                 v.cursor.DeleteSelection()
377                                 v.cursor.ResetSelection()
378                         }
379                         v.eh.Insert(v.cursor.Loc(), "\n")
380                         v.cursor.Right()
381                         // Rehighlight the entire buffer
382                         v.UpdateLines(v.topline, v.topline+v.height)
383                         v.cursor.lastVisualX = v.cursor.GetVisualX()
384                         // v.UpdateLines(v.cursor.y-1, v.cursor.y)
385                 case tcell.KeySpace:
386                         // Insert a space
387                         if v.cursor.HasSelection() {
388                                 v.cursor.DeleteSelection()
389                                 v.cursor.ResetSelection()
390                         }
391                         v.eh.Insert(v.cursor.Loc(), " ")
392                         v.cursor.Right()
393                         v.UpdateLines(v.cursor.y, v.cursor.y)
394                 case tcell.KeyBackspace2:
395                         // Delete a character
396                         if v.cursor.HasSelection() {
397                                 v.cursor.DeleteSelection()
398                                 v.cursor.ResetSelection()
399                                 // Rehighlight the entire buffer
400                                 v.UpdateLines(v.topline, v.topline+v.height)
401                         } else if v.cursor.Loc() > 0 {
402                                 // We have to do something a bit hacky here because we want to
403                                 // delete the line by first moving left and then deleting backwards
404                                 // but the undo redo would place the cursor in the wrong place
405                                 // So instead we move left, save the position, move back, delete
406                                 // and restore the position
407                                 v.cursor.Left()
408                                 cx, cy := v.cursor.x, v.cursor.y
409                                 v.cursor.Right()
410                                 loc := v.cursor.Loc()
411                                 v.eh.Remove(loc-1, loc)
412                                 v.cursor.x, v.cursor.y = cx, cy
413                                 // Rehighlight the entire buffer
414                                 v.UpdateLines(v.topline, v.topline+v.height)
415                                 // v.UpdateLines(v.cursor.y, v.cursor.y+1)
416                         }
417                         v.cursor.lastVisualX = v.cursor.GetVisualX()
418                 case tcell.KeyTab:
419                         // Insert a tab
420                         if v.cursor.HasSelection() {
421                                 v.cursor.DeleteSelection()
422                                 v.cursor.ResetSelection()
423                         }
424                         v.eh.Insert(v.cursor.Loc(), "\t")
425                         v.cursor.Right()
426                         v.UpdateLines(v.cursor.y, v.cursor.y)
427                 case tcell.KeyCtrlS:
428                         v.Save()
429                 case tcell.KeyCtrlF:
430                         if v.cursor.HasSelection() {
431                                 searchStart = v.cursor.curSelection[1]
432                         } else {
433                                 searchStart = ToCharPos(v.cursor.x, v.cursor.y, v.buf)
434                         }
435                         BeginSearch()
436                 case tcell.KeyCtrlN:
437                         if v.cursor.HasSelection() {
438                                 searchStart = v.cursor.curSelection[1]
439                         } else {
440                                 searchStart = ToCharPos(v.cursor.x, v.cursor.y, v.buf)
441                         }
442                         messenger.Message("Find: " + lastSearch)
443                         Search(lastSearch, v, true)
444                 case tcell.KeyCtrlP:
445                         if v.cursor.HasSelection() {
446                                 searchStart = v.cursor.curSelection[0]
447                         } else {
448                                 searchStart = ToCharPos(v.cursor.x, v.cursor.y, v.buf)
449                         }
450                         messenger.Message("Find: " + lastSearch)
451                         Search(lastSearch, v, false)
452                 case tcell.KeyCtrlZ:
453                         v.eh.Undo()
454                         // Rehighlight the entire buffer
455                         v.UpdateLines(v.topline, v.topline+v.height)
456                 case tcell.KeyCtrlY:
457                         v.eh.Redo()
458                         // Rehighlight the entire buffer
459                         v.UpdateLines(v.topline, v.topline+v.height)
460                 case tcell.KeyCtrlC:
461                         v.Copy()
462                         // Rehighlight the entire buffer
463                         v.UpdateLines(v.topline, v.topline+v.height)
464                 case tcell.KeyCtrlX:
465                         v.Cut()
466                         // Rehighlight the entire buffer
467                         v.UpdateLines(v.topline, v.topline+v.height)
468                 case tcell.KeyCtrlV:
469                         v.Paste()
470                         // Rehighlight the entire buffer
471                         v.UpdateLines(v.topline, v.topline+v.height)
472                 case tcell.KeyCtrlA:
473                         v.SelectAll()
474                 case tcell.KeyCtrlO:
475                         v.OpenFile()
476                         // Rehighlight the entire buffer
477                         v.UpdateLines(v.topline, v.topline+v.height)
478                 case tcell.KeyPgUp:
479                         v.PageUp()
480                         relocate = false
481                 case tcell.KeyPgDn:
482                         v.PageDown()
483                         relocate = false
484                 case tcell.KeyCtrlU:
485                         v.HalfPageUp()
486                         relocate = false
487                 case tcell.KeyCtrlD:
488                         v.HalfPageDown()
489                         relocate = false
490                 case tcell.KeyRune:
491                         // Insert a character
492                         if v.cursor.HasSelection() {
493                                 v.cursor.DeleteSelection()
494                                 v.cursor.ResetSelection()
495                                 // Rehighlight the entire buffer
496                                 v.UpdateLines(v.topline, v.topline+v.height)
497                         } else {
498                                 v.UpdateLines(v.cursor.y, v.cursor.y)
499                         }
500                         v.eh.Insert(v.cursor.Loc(), string(e.Rune()))
501                         v.cursor.Right()
502                 }
503         case *tcell.EventMouse:
504                 x, y := e.Position()
505                 x -= v.lineNumOffset - v.leftCol
506                 y += v.topline
507                 // Position always seems to be off by one
508                 x--
509                 y--
510
511                 button := e.Buttons()
512
513                 switch button {
514                 case tcell.Button1:
515                         // Left click
516                         origX, origY := v.cursor.x, v.cursor.y
517                         v.MoveToMouseClick(x, y)
518
519                         if v.mouseReleased {
520                                 if (time.Since(v.lastClickTime)/time.Millisecond < doubleClickThreshold) &&
521                                         (origX == v.cursor.x && origY == v.cursor.y) {
522                                         if v.doubleClick {
523                                                 // Triple click
524                                                 v.lastClickTime = time.Now()
525
526                                                 v.tripleClick = true
527                                                 v.doubleClick = false
528
529                                                 v.cursor.SelectLine()
530                                         } else {
531                                                 // Double click
532                                                 v.lastClickTime = time.Now()
533
534                                                 v.doubleClick = true
535                                                 v.tripleClick = false
536
537                                                 v.cursor.SelectWord()
538                                         }
539                                 } else {
540                                         v.doubleClick = false
541                                         v.tripleClick = false
542                                         v.lastClickTime = time.Now()
543
544                                         loc := v.cursor.Loc()
545                                         v.cursor.curSelection[0] = loc
546                                         v.cursor.curSelection[1] = loc
547                                 }
548                         } else {
549                                 if v.tripleClick {
550                                         v.cursor.AddLineToSelection()
551                                 } else if v.doubleClick {
552                                         v.cursor.AddWordToSelection()
553                                 } else {
554                                         v.cursor.curSelection[1] = v.cursor.Loc()
555                                 }
556                         }
557                         v.mouseReleased = false
558                 case tcell.ButtonNone:
559                         // Mouse event with no click
560                         if !v.mouseReleased {
561                                 // Mouse was just released
562
563                                 // Relocating here isn't really necessary because the cursor will
564                                 // be in the right place from the last mouse event
565                                 // However, if we are running in a terminal that doesn't support mouse motion
566                                 // events, this still allows the user to make selections, except only after they
567                                 // release the mouse
568
569                                 if !v.doubleClick && !v.tripleClick {
570                                         v.MoveToMouseClick(x, y)
571                                         v.cursor.curSelection[1] = v.cursor.Loc()
572                                 }
573                                 v.mouseReleased = true
574                         }
575                         // We don't want to relocate because otherwise the view will be relocated
576                         // every time the user moves the cursor
577                         relocate = false
578                 case tcell.WheelUp:
579                         // Scroll up two lines
580                         v.ScrollUp(2)
581                         // We don't want to relocate if the user is scrolling
582                         relocate = false
583                         // Rehighlight the entire buffer
584                         v.UpdateLines(v.topline, v.topline+v.height)
585                 case tcell.WheelDown:
586                         // Scroll down two lines
587                         v.ScrollDown(2)
588                         // We don't want to relocate if the user is scrolling
589                         relocate = false
590                         // Rehighlight the entire buffer
591                         v.UpdateLines(v.topline, v.topline+v.height)
592                 }
593         }
594
595         if relocate {
596                 v.Relocate()
597         }
598         if settings.Syntax {
599                 v.matches = Match(v)
600         }
601 }
602
603 // DisplayView renders the view to the screen
604 func (v *View) DisplayView() {
605         // matches := make(SyntaxMatches, len(v.buf.lines))
606         //
607         // viewStart := v.topline
608         // viewEnd := v.topline + v.height
609         // if viewEnd > len(v.buf.lines) {
610         //      viewEnd = len(v.buf.lines)
611         // }
612         //
613         // lines := v.buf.lines[viewStart:viewEnd]
614         // for i, line := range lines {
615         //      matches[i] = make([]tcell.Style, len(line))
616         // }
617
618         // The character number of the character in the top left of the screen
619
620         charNum := ToCharPos(0, v.topline, v.buf)
621
622         // Convert the length of buffer to a string, and get the length of the string
623         // We are going to have to offset by that amount
624         maxLineLength := len(strconv.Itoa(len(v.buf.lines)))
625         // + 1 for the little space after the line number
626         v.lineNumOffset = maxLineLength + 1
627
628         var highlightStyle tcell.Style
629
630         for lineN := 0; lineN < v.height; lineN++ {
631                 var x int
632                 // If the buffer is smaller than the view height
633                 // and we went too far, break
634                 if lineN+v.topline >= len(v.buf.lines) {
635                         break
636                 }
637                 line := v.buf.lines[lineN+v.topline]
638
639                 // Write the line number
640                 lineNumStyle := defStyle
641                 if style, ok := colorscheme["line-number"]; ok {
642                         lineNumStyle = style
643                 }
644                 // Write the spaces before the line number if necessary
645                 lineNum := strconv.Itoa(lineN + v.topline + 1)
646                 for i := 0; i < maxLineLength-len(lineNum); i++ {
647                         screen.SetContent(x, lineN, ' ', nil, lineNumStyle)
648                         x++
649                 }
650                 // Write the actual line number
651                 for _, ch := range lineNum {
652                         screen.SetContent(x, lineN, ch, nil, lineNumStyle)
653                         x++
654                 }
655                 // Write the extra space
656                 screen.SetContent(x, lineN, ' ', nil, lineNumStyle)
657                 x++
658
659                 // Write the line
660                 tabchars := 0
661                 runes := []rune(line)
662                 for colN := v.leftCol; colN < v.leftCol+v.width; colN++ {
663                         if colN >= len(runes) {
664                                 break
665                         }
666                         ch := runes[colN]
667                         var lineStyle tcell.Style
668                         // Does the current character need to be syntax highlighted?
669
670                         // if lineN >= v.updateLines[0] && lineN < v.updateLines[1] {
671                         if settings.Syntax {
672                                 highlightStyle = v.matches[lineN][colN]
673                         }
674                         // } else if lineN < len(v.lastMatches) && colN < len(v.lastMatches[lineN]) {
675                         // highlightStyle = v.lastMatches[lineN][colN]
676                         // } else {
677                         // highlightStyle = defStyle
678                         // }
679
680                         if v.cursor.HasSelection() &&
681                                 (charNum >= v.cursor.curSelection[0] && charNum < v.cursor.curSelection[1] ||
682                                         charNum < v.cursor.curSelection[0] && charNum >= v.cursor.curSelection[1]) {
683
684                                 lineStyle = defStyle.Reverse(true)
685
686                                 if style, ok := colorscheme["selection"]; ok {
687                                         lineStyle = style
688                                 }
689                         } else {
690                                 lineStyle = highlightStyle
691                         }
692                         // matches[lineN][colN] = highlightStyle
693
694                         if ch == '\t' {
695                                 screen.SetContent(x+tabchars, lineN, ' ', nil, lineStyle)
696                                 tabSize := settings.TabSize
697                                 for i := 0; i < tabSize-1; i++ {
698                                         tabchars++
699                                         if x-v.leftCol+tabchars >= v.lineNumOffset {
700                                                 screen.SetContent(x-v.leftCol+tabchars, lineN, ' ', nil, lineStyle)
701                                         }
702                                 }
703                         } else {
704                                 if x-v.leftCol+tabchars >= v.lineNumOffset {
705                                         screen.SetContent(x-v.leftCol+tabchars, lineN, ch, nil, lineStyle)
706                                 }
707                         }
708                         charNum++
709                         x++
710                 }
711                 // Here we are at a newline
712
713                 // The newline may be selected, in which case we should draw the selection style
714                 // with a space to represent it
715                 if v.cursor.HasSelection() &&
716                         (charNum >= v.cursor.curSelection[0] && charNum < v.cursor.curSelection[1] ||
717                                 charNum < v.cursor.curSelection[0] && charNum >= v.cursor.curSelection[1]) {
718
719                         selectStyle := defStyle.Reverse(true)
720
721                         if style, ok := colorscheme["selection"]; ok {
722                                 selectStyle = style
723                         }
724                         screen.SetContent(x-v.leftCol+tabchars, lineN, ' ', nil, selectStyle)
725                 }
726
727                 charNum++
728         }
729         // v.lastMatches = matches
730 }
731
732 // Display renders the view, the cursor, and statusline
733 func (v *View) Display() {
734         v.DisplayView()
735         v.cursor.Display()
736         v.sline.Display()
737 }