...
1 package prompt
2
3 import (
4 "strings"
5
6 "github.com/c-bata/go-prompt/internal/debug"
7 )
8
9
10 type Buffer struct {
11 workingLines []string
12 workingIndex int
13 cursorPosition int
14 cacheDocument *Document
15 preferredColumn int
16 lastKeyStroke Key
17 }
18
19
20 func (b *Buffer) Text() string {
21 return b.workingLines[b.workingIndex]
22 }
23
24
25 func (b *Buffer) Document() (d *Document) {
26 if b.cacheDocument == nil ||
27 b.cacheDocument.Text != b.Text() ||
28 b.cacheDocument.cursorPosition != b.cursorPosition {
29 b.cacheDocument = &Document{
30 Text: b.Text(),
31 cursorPosition: b.cursorPosition,
32 }
33 }
34 b.cacheDocument.lastKey = b.lastKeyStroke
35 return b.cacheDocument
36 }
37
38
39
40 func (b *Buffer) DisplayCursorPosition() int {
41 return b.Document().DisplayCursorPosition()
42 }
43
44
45 func (b *Buffer) InsertText(v string, overwrite bool, moveCursor bool) {
46 or := []rune(b.Text())
47 oc := b.cursorPosition
48
49 if overwrite {
50 overwritten := string(or[oc : oc+len(v)])
51 if strings.Contains(overwritten, "\n") {
52 i := strings.IndexAny(overwritten, "\n")
53 overwritten = overwritten[:i]
54 }
55 b.setText(string(or[:oc]) + v + string(or[oc+len(overwritten):]))
56 } else {
57 b.setText(string(or[:oc]) + v + string(or[oc:]))
58 }
59
60 if moveCursor {
61 b.cursorPosition += len([]rune(v))
62 }
63 }
64
65
66
67
68 func (b *Buffer) setText(v string) {
69 debug.Assert(b.cursorPosition <= len([]rune(v)), "length of input should be shorter than cursor position")
70 b.workingLines[b.workingIndex] = v
71 }
72
73
74 func (b *Buffer) setCursorPosition(p int) {
75 if p > 0 {
76 b.cursorPosition = p
77 } else {
78 b.cursorPosition = 0
79 }
80 }
81
82 func (b *Buffer) setDocument(d *Document) {
83 b.cacheDocument = d
84 b.setCursorPosition(d.cursorPosition)
85 b.setText(d.Text)
86 }
87
88
89 func (b *Buffer) CursorLeft(count int) {
90 l := b.Document().GetCursorLeftPosition(count)
91 b.cursorPosition += l
92 }
93
94
95 func (b *Buffer) CursorRight(count int) {
96 l := b.Document().GetCursorRightPosition(count)
97 b.cursorPosition += l
98 }
99
100
101
102 func (b *Buffer) CursorUp(count int) {
103 orig := b.preferredColumn
104 if b.preferredColumn == -1 {
105 orig = b.Document().CursorPositionCol()
106 }
107 b.cursorPosition += b.Document().GetCursorUpPosition(count, orig)
108
109
110 b.preferredColumn = orig
111 }
112
113
114
115 func (b *Buffer) CursorDown(count int) {
116 orig := b.preferredColumn
117 if b.preferredColumn == -1 {
118 orig = b.Document().CursorPositionCol()
119 }
120 b.cursorPosition += b.Document().GetCursorDownPosition(count, orig)
121
122
123 b.preferredColumn = orig
124 }
125
126
127 func (b *Buffer) DeleteBeforeCursor(count int) (deleted string) {
128 debug.Assert(count >= 0, "count should be positive")
129 r := []rune(b.Text())
130
131 if b.cursorPosition > 0 {
132 start := b.cursorPosition - count
133 if start < 0 {
134 start = 0
135 }
136 deleted = string(r[start:b.cursorPosition])
137 b.setDocument(&Document{
138 Text: string(r[:start]) + string(r[b.cursorPosition:]),
139 cursorPosition: b.cursorPosition - len([]rune(deleted)),
140 })
141 }
142 return
143 }
144
145
146 func (b *Buffer) NewLine(copyMargin bool) {
147 if copyMargin {
148 b.InsertText("\n"+b.Document().leadingWhitespaceInCurrentLine(), false, true)
149 } else {
150 b.InsertText("\n", false, true)
151 }
152 }
153
154
155 func (b *Buffer) Delete(count int) (deleted string) {
156 r := []rune(b.Text())
157 if b.cursorPosition < len(r) {
158 deleted = b.Document().TextAfterCursor()[:count]
159 b.setText(string(r[:b.cursorPosition]) + string(r[b.cursorPosition+len(deleted):]))
160 }
161 return
162 }
163
164
165 func (b *Buffer) JoinNextLine(separator string) {
166 if !b.Document().OnLastLine() {
167 b.cursorPosition += b.Document().GetEndOfLinePosition()
168 b.Delete(1)
169
170 b.setText(b.Document().TextBeforeCursor() + separator + strings.TrimLeft(b.Document().TextAfterCursor(), " "))
171 }
172 }
173
174
175 func (b *Buffer) SwapCharactersBeforeCursor() {
176 if b.cursorPosition >= 2 {
177 x := b.Text()[b.cursorPosition-2 : b.cursorPosition-1]
178 y := b.Text()[b.cursorPosition-1 : b.cursorPosition]
179 b.setText(b.Text()[:b.cursorPosition-2] + y + x + b.Text()[b.cursorPosition:])
180 }
181 }
182
183
184 func NewBuffer() (b *Buffer) {
185 b = &Buffer{
186 workingLines: []string{""},
187 workingIndex: 0,
188 preferredColumn: -1,
189 }
190 return
191 }
192
View as plain text