]> git.lizzy.rs Git - micro.git/blob - cursor.go
Really improve syntax file compatibility with nano
[micro.git] / cursor.go
1 package main
2
3 import (
4         "strings"
5 )
6
7 // The Cursor struct stores the location of the cursor in the view
8 type Cursor struct {
9         v *View
10
11         // We need three variables here because we insert text at loc but
12         // display the cursor at x, y
13         x   int
14         y   int
15         loc int
16
17         // Start of the selection in charNum
18         selectionStart int
19
20         // We store the x, y of the start because when the user deletes the selection
21         // the cursor needs to go back to the start, and this is the simplest way
22         selectionStartX int
23         selectionStartY int
24
25         // End of the selection in charNum
26         // We don't need to store the x, y here because when if the user is selecting backwards
27         // and they delete the selection, the cursor is already in the right place
28         selectionEnd int
29 }
30
31 // ResetSelection resets the user's selection
32 func (c *Cursor) ResetSelection() {
33         c.selectionStart = 0
34         c.selectionEnd = 0
35 }
36
37 // HasSelection returns whether or not the user has selected anything
38 func (c *Cursor) HasSelection() bool {
39         return c.selectionEnd != c.selectionStart
40 }
41
42 // DeleteSelection deletes the currently selected text
43 func (c *Cursor) DeleteSelection() {
44         if c.selectionStart > c.selectionEnd {
45                 c.v.eh.Remove(c.selectionEnd, c.selectionStart+1)
46                 // Since the cursor is already at the selection start we don't need to move
47         } else {
48                 c.v.eh.Remove(c.selectionStart, c.selectionEnd+1)
49                 c.loc -= c.selectionEnd - c.selectionStart
50                 c.x = c.selectionStartX
51                 c.y = c.selectionStartY
52         }
53 }
54
55 // GetSelection returns the cursor's selection
56 func (c *Cursor) GetSelection() string {
57         if c.selectionStart > c.selectionEnd {
58                 return string([]rune(c.v.buf.text)[c.selectionEnd : c.selectionStart+1])
59         }
60         return string([]rune(c.v.buf.text)[c.selectionStart : c.selectionEnd+1])
61 }
62
63 // RuneUnder returns the rune under the cursor
64 func (c *Cursor) RuneUnder() rune {
65         line := c.v.buf.lines[c.y]
66         if c.x >= Count(line) {
67                 return ' '
68         }
69         return []rune(line)[c.x]
70 }
71
72 // Up moves the cursor up one line (if possible)
73 func (c *Cursor) Up() {
74         if c.y > 0 {
75                 c.loc -= Count(c.v.buf.lines[c.y][:c.x])
76                 // Count the newline
77                 c.loc--
78                 c.y--
79
80                 if c.x > Count(c.v.buf.lines[c.y]) {
81                         c.x = Count(c.v.buf.lines[c.y])
82                 }
83
84                 c.loc -= Count(c.v.buf.lines[c.y][c.x:])
85         }
86 }
87
88 // Down moves the cursor down one line (if possible)
89 func (c *Cursor) Down() {
90         if c.y < len(c.v.buf.lines)-1 {
91                 c.loc += Count(c.v.buf.lines[c.y][c.x:])
92                 // Count the newline
93                 c.loc++
94                 c.y++
95
96                 if c.x > Count(c.v.buf.lines[c.y]) {
97                         c.x = Count(c.v.buf.lines[c.y])
98                 }
99
100                 c.loc += Count(c.v.buf.lines[c.y][:c.x])
101         }
102 }
103
104 // Left moves the cursor left one cell (if possible) or to the last line if it is at the beginning
105 func (c *Cursor) Left() {
106         if c.loc == 0 {
107                 return
108         }
109         if c.x > 0 {
110                 c.loc--
111                 c.x--
112         } else {
113                 c.Up()
114                 c.End()
115         }
116 }
117
118 // Right moves the cursor right one cell (if possible) or to the next line if it is at the end
119 func (c *Cursor) Right() {
120         if c.loc == c.v.buf.Len() {
121                 return
122         }
123         if c.x < Count(c.v.buf.lines[c.y]) {
124                 c.loc++
125                 c.x++
126         } else {
127                 c.Down()
128                 c.Start()
129         }
130 }
131
132 // End moves the cursor to the end of the line it is on
133 func (c *Cursor) End() {
134         c.loc += Count(c.v.buf.lines[c.y][c.x:])
135         c.x = Count(c.v.buf.lines[c.y])
136 }
137
138 // Start moves the cursor to the start of the line it is on
139 func (c *Cursor) Start() {
140         c.loc -= Count(c.v.buf.lines[c.y][:c.x])
141         c.x = 0
142 }
143
144 // GetCharPosInLine gets the char position of a visual x y coordinate (this is necessary because tabs are 1 char but 4 visual spaces)
145 func (c *Cursor) GetCharPosInLine(lineNum, visualPos int) int {
146         visualLine := strings.Replace(c.v.buf.lines[lineNum], "\t", "\t"+EmptyString(tabSize-1), -1)
147         if visualPos > Count(visualLine) {
148                 visualPos = Count(visualLine)
149         }
150         numTabs := NumOccurences(visualLine[:visualPos], '\t')
151         if visualPos >= (tabSize-1)*numTabs {
152                 return visualPos - (tabSize-1)*numTabs
153         }
154         return visualPos / tabSize
155 }
156
157 // GetVisualX returns the x value of the cursor in visual spaces
158 func (c *Cursor) GetVisualX() int {
159         return c.x + NumOccurences(c.v.buf.lines[c.y][:c.x], '\t')*(tabSize-1)
160 }
161
162 // Distance returns the distance between the cursor and x, y in runes
163 func (c *Cursor) Distance(x, y int) int {
164         // Same line
165         if y == c.y {
166                 return x - c.x
167         }
168
169         var distance int
170         if y > c.y {
171                 distance += Count(c.v.buf.lines[c.y][c.x:])
172                 // Newline
173                 distance++
174                 i := 1
175                 for y != c.y+i {
176                         distance += Count(c.v.buf.lines[c.y+i])
177                         // Newline
178                         distance++
179                         i++
180                 }
181                 if x < Count(c.v.buf.lines[y]) {
182                         distance += Count(c.v.buf.lines[y][:x])
183                 } else {
184                         distance += Count(c.v.buf.lines[y])
185                 }
186                 return distance
187         }
188
189         distance -= Count(c.v.buf.lines[c.y][:c.x])
190         // Newline
191         distance--
192         i := 1
193         for y != c.y-i {
194                 distance -= Count(c.v.buf.lines[c.y-i])
195                 // Newline
196                 distance--
197                 i++
198         }
199         if x >= 0 {
200                 distance -= Count(c.v.buf.lines[y][x:])
201         }
202         return distance
203 }
204
205 // Display draws the cursor to the screen at the correct position
206 func (c *Cursor) Display() {
207         if c.y-c.v.topline < 0 || c.y-c.v.topline > c.v.height-1 {
208                 c.v.s.HideCursor()
209         } else {
210                 c.v.s.ShowCursor(c.GetVisualX()+c.v.lineNumOffset, c.y-c.v.topline)
211                 // cursorStyle := tcell.StyleDefault.Reverse(true)
212                 // c.v.s.SetContent(c.x+voffset, c.y-c.v.topline, c.runeUnder(), nil, cursorStyle)
213         }
214 }