cursor.go (3479B)
1 package buffer 2 3 import ( 4 "unicode/utf8" 5 6 "code.dwrz.net/src/pkg/editor/buffer/glyph" 7 ) 8 9 type Cursor struct { 10 glyph int 11 index int 12 line int 13 } 14 15 func (b *Buffer) Cursor() Cursor { 16 return *b.cursor 17 } 18 19 func (c Cursor) Glyph() int { 20 return c.glyph 21 } 22 23 func (c Cursor) Index() int { 24 return c.index 25 } 26 27 func (c Cursor) Line() int { 28 return c.line 29 } 30 31 func (b *Buffer) CursorDown() { 32 b.log.Debug.Printf("↓ before: %#v", b.cursor) 33 defer b.log.Debug.Printf("↓ after: %#v", b.cursor) 34 35 // If we're on the last line, don't move down. 36 if b.cursor.line >= len(b.lines)-1 { 37 return 38 } 39 40 // Move to the next line. 41 b.cursor.line++ 42 43 // Adjust the column. 44 var ( 45 line = b.CursorLine() 46 length = line.Length() 47 width = line.Width() 48 ) 49 switch { 50 // If we've moved to a shorter line, snap to its end. 51 case b.cursor.glyph > width: 52 b.cursor.index = length 53 b.cursor.glyph = width 54 55 default: 56 b.cursor.index, b.cursor.glyph = line.FindGlyphIndex( 57 b.cursor.glyph, 58 ) 59 } 60 } 61 62 func (b *Buffer) CursorLeft() { 63 b.log.Debug.Printf("← before: %#v", b.cursor) 64 defer b.log.Debug.Printf("← after: %#v", b.cursor) 65 66 switch { 67 // If we're at the beginning of the line, move to the line above; 68 // unless we're at the start of the buffer. 69 case b.cursor.index == 0 && b.cursor.line > 0: 70 b.cursor.line-- 71 72 line := b.CursorLine() 73 b.cursor.index = line.Length() 74 b.cursor.glyph = line.Width() 75 76 // Move left by one rune. 77 case b.cursor.index > 0: 78 var ( 79 line = b.CursorLine() 80 r rune 81 size int 82 ) 83 // Reverse until we hit the start of a rune. 84 for i := b.cursor.index - 1; i >= 0; i-- { 85 r, size = line.DecodeRune(i) 86 if r != utf8.RuneError { 87 b.cursor.index -= size 88 b.cursor.glyph -= glyph.Width(r) 89 break 90 } 91 } 92 } 93 } 94 95 func (b *Buffer) CursorRight() { 96 b.log.Debug.Printf("→ before: %#v", b.cursor) 97 defer b.log.Debug.Printf("→ after: %#v", b.cursor) 98 99 var ( 100 line = b.CursorLine() 101 length = line.Length() 102 ) 103 104 switch { 105 // If we're at the end of the line, move to the line below; 106 // unless we're at the end of the buffer. 107 case b.cursor.index == length && b.cursor.line < len(b.lines)-1: 108 b.cursor.line++ 109 b.cursor.index = 0 110 b.cursor.glyph = 0 111 112 // Move the index right by one rune. 113 case b.cursor.index < length: 114 r, size := line.DecodeRune(b.cursor.index) 115 if r == utf8.RuneError { 116 b.cursor.index++ 117 b.cursor.glyph += glyph.Width(r) 118 } 119 120 b.cursor.index += size 121 b.cursor.glyph += glyph.Width(r) 122 } 123 } 124 125 func (b *Buffer) CursorUp() { 126 b.log.Debug.Printf("↑ before: %#v", b.cursor) 127 defer b.log.Debug.Printf("↑ after: %#v", b.cursor) 128 129 // If we're on the first line, don't move up. 130 if b.cursor.line == 0 { 131 return 132 } 133 134 b.cursor.line-- 135 136 // Adjust the column. 137 var ( 138 line = b.CursorLine() 139 length = line.Length() 140 width = line.Width() 141 ) 142 switch { 143 case b.cursor.glyph > width: 144 b.cursor.index = length 145 b.cursor.glyph = width 146 147 default: 148 b.cursor.index, b.cursor.glyph = line.FindGlyphIndex( 149 b.cursor.glyph, 150 ) 151 } 152 } 153 154 func (b *Buffer) PageDown(height int) { 155 if b.cursor.line+height > len(b.lines) { 156 b.cursor.line = len(b.lines) - 1 157 } else { 158 b.cursor.line += height 159 } 160 } 161 162 func (b *Buffer) PageUp(height int) { 163 if b.cursor.line-height > 0 { 164 b.cursor.line -= height 165 } else { 166 b.cursor.line = 0 167 } 168 } 169 170 func (b *Buffer) CursorHome() { 171 b.cursor.glyph = 0 172 b.cursor.index = 0 173 } 174 175 func (b *Buffer) CursorEnd() { 176 var line = b.CursorLine() 177 178 b.cursor.index = line.Length() 179 b.cursor.glyph = line.Width() 180 }