Used colon-less keyword syntax in method signatures where the optional variable name...
[cslatevm.git] / src / lib / editor.slate
blobe184b8a186b1ae10ec42d4c2dd6ce95b57d77bec
1 "a line editor for src/ui/textbox.slate"
3 prototypes ensureNamespace: #Editor &delegate: True.
6 Editor define: #Cursor &parents: {Cloneable} &slots: {#line -> 0. #column -> 0}.
7 Editor define: #Line &parents: {Cloneable} &slots: {#contents -> ''}.
8 Editor define: #EditorAction &parents: {Cloneable} &slots: {#redo -> Nil. #undo -> Nil}.
10 Editor define: #LineEditor &parents: {Cloneable}
11   &slots: {#lines -> ExtensibleArray new. 
12            #cursors -> Stack new.
13            "when the cursor moves these may be updated... they help decide what needs to be painted"
14            #lineDelimeter -> '\n'.
15            #firstVisibleLine -> 0.
16            #visibleLines -> 10.
17            #undoHistory -> ExtensibleArray new.
18            #undoHistoryPosition -> 0.
19            }.
22 x@(LineEditor traits) new
23 [ |le|
24   le := resend.
25   le reset.
26   le
29 le@(LineEditor traits) reset
31   le lines: (ExtensibleArray newWith: Line new).
32   le cursors: (Stack newWith: (Cursor line: 0 column: 0)).
33   le undoHistory: ExtensibleArray new.
34   le
37 le@(LineEditor traits) cursor
38 ["return the current cursor point"
39   le cursors top
42 c@(Cursor traits) line: line column: column
44   c clone `setting: #{#line. #column} to: {line. column}
47 c1@(Cursor traits) min: c2@(Cursor traits)
49   c1 line < c2 line ifTrue: [^ c1].
50   c1 line > c2 line ifTrue: [^ c2].
51   c1 column < c2 column ifTrue: [^ c1].
52   c2
55 c1@(Cursor traits) max: c2@(Cursor traits)
57   c1 line < c2 line ifTrue: [^ c2].
58   c1 line > c2 line ifTrue: [^ c1].
59   c1 column < c2 column ifTrue: [^ c2].
60   c1
63 c1@(Cursor traits) = c2@(Cursor traits)
65   c1 line = c2 line /\ [c1 column = c2 column]
68 ea@(EditorAction traits) redo: redo undo: undo
70   ea clone `setting: #{#redo. #undo} to: {redo. undo}
73 "------------------------------------------------------------"
74 "functions that you can replace if you change the underlying data types. all functions on the buffer
75 should use these functions instead of accessing the lines directly. these should NOT modify the cursor point"
76 "------------------------------------------------------------"
78 l@(Line traits) size [l contents size].
79 l@(Line traits) first: n [l contents first: n].
80 l@(Line traits) allButFirst: n [l contents allButFirst: n].
81 l@(Line traits) merge: l2@(Line traits) [l contents: l contents ; l2 contents].
82 l@(Line traits) as: _@(String traits) [l contents].
84 l@(Line traits) deleteAt: n
86   l contents: (l first: n) ; (l allButFirst: n + 1).
87   l
90 l@(Line traits) insert: c at: n
92   l contents: (l first: n) ; (c as: String) ; (l allButFirst: n).
93   l
96 le@(LineEditor traits) lineCount [le lines size].
98 le@(LineEditor traits) lineAt: line
100   le lines at: line
103 le@(LineEditor traits) textOnLineAfter: c@(Cursor traits)
104 ["this includes the point"
105   (le lineAt: c line) allButFirst: c column
108 le@(LineEditor traits) textOnLineBefore: c@(Cursor traits)
110   (le lineAt: c line) first: c column
113 le@(LineEditor traits) deleteFrom: c@(Cursor traits)
115   (le lineAt: c line) deleteAt: c column.
118 le@(LineEditor traits) characterAt: c@(Cursor traits)
119 [ |line|
120   line := (le lineAt: c line).
121   line contents size <= c column ifTrue: [line contents size = 0
122                                                ifTrue: [^ (line contents at: line contents size - 1)]
123                                                ifFalse: [^ $\n]].
124   line contents at: c column
128 le@(LineEditor traits) insertCharacter: c@(ASCIICharacter traits) at: point@(Cursor traits)
130   (le lineAt: point line) insert: c at: point column.
134 le@(LineEditor traits) setTextOnLine: line to: s
136   (le lineAt: line) contents: s
139 le@(LineEditor traits) insertLineAt: line withText: s
141   le lines at: line insert: (Line new `>> [contents: s. ]).
144 le@(LineEditor traits) breakLine: line at: column
145 [ |restOfLine p |
146   p := (Cursor line: line column: column).
147   restOfLine := (le textOnLineAfter: p).
148   le insertLineAt: line withText: (le textOnLineBefore: p).
149   le setTextOnLine: line + 1 to: restOfLine.
152 le@(LineEditor traits) mergeLine: line with: line2
154   (le lineAt: line) merge: (le lineAt: line2).
155    le deleteLine: line2.
158 le@(LineEditor traits) deleteLine: line
160   le lines at: line remove: 1
163 p@(Cursor traits) forwardOn: le@(LineEditor traits)
164 [ |point|
165   point := p copy. 
166   (le lineAt: point line) size > point column ifTrue: [point column: point column + 1. ^ point].
167   le lineCount <= (point line + 1) ifTrue: [inform: 'End of buffer'. ^ point].
168   point line: point line + 1.
169   point column: 0.
170   point
173 p@(Cursor traits) forwardOnSameLine: le@(LineEditor traits)
174 [ |point|
175   point := p copy. 
176   (le lineAt: point line) size > point column ifTrue: [point column: point column + 1. ^ point].
177   point
180 p@(Cursor traits) backwardOn: le@(LineEditor traits)
181 [ |point|
182   point := p copy.
183   point column > 0 ifTrue: [point column: point column - 1. ^ point].
184   point line < 1 ifTrue: [inform: 'Beginning of buffer'. ^ point].
185   point line: point line - 1.
186   point column: (le lineAt: point line) size.
187   point
190 p@(Cursor traits) nextLineOn: le@(LineEditor traits)
191 [ |point|
192   point := p copy.
193   point line: (point line + 1 min: le lineCount - 1).
194   point
197 p@(Cursor traits) previousLineOn: le@(LineEditor traits)
198 [ |point|
199   point := p copy.
200   point line: (point line - 1 max: 0).
201   point
206 p@(Cursor traits) setTo: point@(Cursor traits)
208   p line: point line.
209   p column: point column.
210   p
213 le@(LineEditor traits) do: block undo: undoBlock record: record
214 [ | undoFromEnd |
215   record `defaultsTo: True.
216   record ifFalse: [^ block do].
217   undoFromEnd := le undoHistory size - le undoHistoryPosition.
218   undoFromEnd > 0 ifTrue: [le undoHistory removeLast: undoFromEnd].
220   "fixme: perhaps catch errors from doing the block and altering undo history?"
221   le undoHistoryPosition: le undoHistoryPosition + 1.
222   le undoHistory addLast: (EditorAction redo: block undo: undoBlock).
223   block do
224   
227 le@(LineEditor traits) undo
229   le undoHistoryPosition < 1 ifTrue: [^ Nil].
230   le undoHistoryPosition: le undoHistoryPosition - 1.
231   (le undoHistory at: le undoHistoryPosition) undo do.
234 le@(LineEditor traits) redo
236   le undoHistoryPosition >= le undoHistory size ifTrue: [^ Nil].
237   (le undoHistory at: le undoHistoryPosition) redo do.
238   le undoHistoryPosition: le undoHistoryPosition + 1.
242 "------------------------------------------------------------"
243 "functions here use the functions above to modify the buffer.
244 these functions will create undo history and have side effects
245 on the point.
247 record -> should this be recorded in the undo history?"
248 "------------------------------------------------------------"
250 "fixme use forward on same line and stuff"
252 le@(LineEditor traits) insert: str@(ASCIIString traits) at: point@(Cursor traits) &record
253 [ |s| "fixme this is slow"
254   s := (str copyReplaceAll: le lineDelimeter with: '\r').
255   le do: [s do: [|:c| le insert: c at: point &record: False]]
256      undo: [s size timesRepeat: [le deleteBackwardAt: point &record: False]]
257      record: record.
260 le@(LineEditor traits) insert: c@(ASCIICharacter traits) at: point@(Cursor traits) &record
261 [ | p |
262   p := point copy. "make a copy so the undo history doesn't get one with side effects"
263   c = $\r ifTrue: [
264                     le do: [le breakLine: p line at: p column.
265                             point setTo: (p forwardOn: le)]
266                        undo: [le deleteFrom: p. 
267                               point setTo: p]
268                        record: record]
269            ifFalse: [le do: [le insertCharacter: c at: p.
270                              point setTo: (p forwardOnSameLine: le)]
271                         undo: [le deleteFrom: p.
272                                point setTo: p]
273                         record: record].
278 le@(LineEditor traits) deleteBackwardAt: point@(Cursor traits) &record
279 [ | deletedCharacter p prevLineSize|
280   p := point copy. "make a copy so the undo history doesn't get one with side effects"
281   p column < 1 ifTrue: [p line = 0 ifTrue: [inform: 'Cannot delete beginning of buffer'. ^ Nil].
282                         prevLineSize := (le lineAt: point line - 1) size.
283                         le do: [point setTo: (p backwardOn: le).
284                                 le mergeLine: p line - 1 with: p line]
285                            undo: [le breakLine: p line - 1 at: prevLineSize.
286                                   point setTo: p]
287                           record: record]
288                ifFalse: [deletedCharacter: (le characterAt: (p backwardOn: le)).
289                          inform: 'deleted: ' ; deletedCharacter printString.
290                          le do: [le deleteFrom: (p backwardOn: le).
291                                  point setTo: (p backwardOn: le)]
292                             undo: [le insertCharacter: deletedCharacter at: (p backwardOn: le).
293                                    point setTo: p]
294                             record: record].
298 "fixme: deleteFrom name might be confused with this"
299 le@(LineEditor traits) deleteAt: point@(Cursor traits) &record
300 [ | deletedCharacter p lineSize|
301   p := point copy. "make a copy so the undo history doesn't get one with side effects"
302   p column >= (le lineAt: p line) size
303     ifTrue: [p line + 1 >= le lineCount ifTrue: [inform: 'Cannot delete end of buffer'. ^ Nil].
304              lineSize := (le lineAt: point line) size.
305              le do: [le mergeLine: p line with: p line + 1.
306                      point setTo: p]
307                 undo: [le breakLine: p line at: lineSize.
308                        point setTo: p]
309                 record: record]
310     ifFalse: [deletedCharacter: (le characterAt: p).
311               inform: 'deleted: ' ; deletedCharacter printString.
312               le do: [le deleteFrom: p.
313                       point setTo: p]
314                  undo: [le insertCharacter: deletedCharacter at: p.
315                         point setTo: p]
316                  record: record].
317   
320 le@(LineEditor traits) textFrom: start@(Cursor traits) to: end@(Cursor traits) 
321 [ |startLineText endLineText |
322   (start min: end) == end ifTrue: [error: 'Cannot get text because start point is >= end' ; start printString ; end printString].
323   start line = end line ifTrue: [^ ((le lineAt: start line) contents copyFrom: start column to: end column - 1)].
324   startLineText := ((le lineAt: start line) contents allButFirst: start column).
325   endLineText := ((le lineAt: end line) contents first: end column).
326   startLineText concatenateAll: 
327     ((start line + 1 to: end line - 1) collect: [|:i| (le lineAt: i) contents]) ; {endLineText}
328     &separator: le lineDelimeter
332 le@(LineEditor traits) wipeFrom: start@(Cursor traits) to: end@(Cursor traits) &record
333 [ | text startSaved |
334   text := (le textFrom: start to: end).
335   inform: 'text: ' ; text.
337   le do: [text size timesRepeat: [le deleteBackwardAt: end &record: False]]
338      undo: [|point|
339               point := start copy.
340               text do: [|:c| le insert: c at: point &record: False]]
341      record: record.
342   text
346 le@(LineEditor traits) setMark &record
348   le do: [le cursors push: le cursor copy] undo: [le cursors pop] record: record
351 le@(LineEditor traits) deleteMark &record
352 [ |mark| 
353   le cursors size > 1 ifFalse: [inform: 'Cannot delete last mark'. ^ Nil].
354   mark := le cursor.
355   le do: [le cursors pop] undo: [le cursors push: mark copy] record: record
358 le@(LineEditor traits) cutTo: window &record
359 [ |minPoint maxPoint text|
360   le cursors size < 2 ifTrue: [inform: 'cannot cut... no marked region'. ^ Nil].
361   minPoint := (le cursors top min: (le cursors fromTop: 1)).
362   maxPoint := (le cursors top max: (le cursors fromTop: 1)).
363   text := (le wipeFrom: minPoint to: maxPoint &record: record).
364   inform: 'text: ' ; text.
365   window clipboardCopy: text.
366   le cursors pop.
369 le@(LineEditor traits) copyTo: window &record
370 [ |text maxPoint minPoint|
371   le cursors size < 2 ifTrue: [inform: 'cannot copy... no marked region'. ^ Nil].
372   minPoint := (le cursors top min: (le cursors fromTop: 1)).
373   maxPoint := (le cursors top max: (le cursors fromTop: 1)).
374   text := (le textFrom: minPoint to: maxPoint).
375   inform: 'text: ' ; text.
376   window clipboardCopy: text.
377   le cursors pop.
380 le@(LineEditor traits) pasteFrom: window &record
381 [ |str|
382   str := (window clipboardPasteAs: String).
383   le insert: (str before: $\0 ifAbsent: [str]) at: le cursor.