Updated TODO.
[mp-5.x.git] / mp_edit.mpsl
blob020d29f8909e2e5aa9c74eea81f10f4b3ddfd2ef
1 /*
3     Minimum Profit 5.x
4     A Programmer's Text Editor
6     Editing.
8     Copyright (C) 1991-2007 Angel Ortega <angel@triptico.com>
10     This program is free software; you can redistribute it and/or
11     modify it under the terms of the GNU General Public License
12     as published by the Free Software Foundation; either version 2
13     of the License, or (at your option) any later version.
15     This program is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18     GNU General Public License for more details.
20     You should have received a copy of the GNU General Public License
21     along with this program; if not, write to the Free Software
22     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24     http://www.triptico.com
29 /* editor actions */
31 mp.actions['insert_line']       = sub (d) {
32         mp.store_undo(d);
33         mp.insert_newline(d);
35         if (d.syntax == NULL) mp.detect_syntax(d);
38 mp.actions['delete_line']       = sub (d) { mp.store_undo(d); mp.delete_line(d); };
39 mp.actions['insert_space']      = sub (d) { mp.store_undo(d); mp.insert_space(d); };
40 mp.actions['insert_tab']        = sub (d) { mp.store_undo(d); mp.insert_tab(d); };
41 mp.actions['delete']            = sub (d) { mp.store_undo(d); mp.delete_char(d); };
42 mp.actions['delete_left']       = sub (d) { mp.store_undo(d); mp.move_left(d) &&
43                                         mp.delete_char(d); };
44 mp.actions['undo']              = sub (d) { mp.undo(d); };
45 mp.actions['redo']              = sub (d) { mp.redo(d); };
46 mp.actions['join_paragraph']    = sub (d) { mp.store_undo(d); mp.join_paragraph(d); };
48 mp.actions['word_wrap_paragraph'] = sub (d) {
50         if(mp.config.word_wrap == 0)
51                 mp.alert(L("Word wrapping must be set"));
52         else
53         {
54                 mp.store_undo(d);
55                 mp.word_wrap_paragraph(d);
56         }
59 mp.actions['line_options']      = sub (d) {
61         /* convert special characters on end of line */
62         local lt = mp.backslash_codes(mp.config.eol, 1);
64         local t = mp.form( [
65                 { 'label'       => L("Word wrap on column (0, no word wrap):"),
66                   'type'        => 'text',
67                   'value'       => mp.config.word_wrap,
68                   'history'     => 'wordwrap' },
69                 { 'label'       => L("Automatic indentation") ~ ':',
70                   'type'        => 'checkbox',
71                   'value'       => mp.config.auto_indent },
72                 { 'label'       => L("Line termination") ~ ':',
73                   'value'       => lt,
74                   'type'        => 'text' }
75         ] );
77         if(t != NULL)
78         {
79                 mp.config.word_wrap = t[0];
80                 mp.config.auto_indent = t[1];
81                 mp.config.eol = mp.backslash_codes(t[2], 0);
82         }
85 mp.actions['tab_options']       = sub (d) {
87         local t = mp.form( [
88                 { 'label'       => L("Tab size") ~ ':',
89                   'type'        => 'text',
90                   'value'       => mp.config.tab_size,
91                   'history'     => 'tabsize' },
92                 { 'label'       => L("Convert tabs to spaces") ~ ':',
93                   'type'        => 'checkbox',
94                   'value'       => mp.config.tabs_as_spaces }
95         ] );
97         if(t != NULL)
98         {
99                 mp.config.tab_size = t[0];
100                 mp.config.tabs_as_spaces = t[1];
101         }
104 /* default key bindings */
106 mp.keycodes['enter']    = "insert_line";
107 mp.keycodes['tab']      = "insert_tab";
108 mp.keycodes['space']    = "insert_space";
109 mp.keycodes['delete']   = "delete";
110 mp.keycodes['backspace']= "delete_left";
111 mp.keycodes['ctrl-i']   = "insert_tab";
112 mp.keycodes['ctrl-m']   = "insert_line";
113 mp.keycodes['ctrl-y']   = "delete_line";
114 mp.keycodes['ctrl-z']   = "undo";
115 mp.keycodes['f4']       = "word_wrap_paragraph";
116 mp.keycodes['f6']       = "join_paragraph";
118 /* action descriptions */
120 mp.actdesc['insert_line']       = LL("Insert line");
121 mp.actdesc['delete_line']       = LL("Delete line");
122 mp.actdesc['insert_space']      = LL("Insert space");
123 mp.actdesc['insert_tab']        = LL("Insert tab");
124 mp.actdesc['delete']            = LL("Delete character");
125 mp.actdesc['delete_left']       = LL("Delete character to the left");
126 mp.actdesc['undo']              = LL("Undo");
127 mp.actdesc['redo']              = LL("Redo");
128 mp.actdesc['join_paragraph']    = LL("Join a paragraph in one line");
129 mp.actdesc['word_wrap_paragraph'] = LL("Word-wrap a paragraph");
130 mp.actdesc['line_options']      = LL("Line options...");
131 mp.actdesc['tab_options']       = LL("Tab options...");
133 /* code */
135 sub mp.break_line(doc, col)
136 /* breaks current line in two (inserts a newline) */
138         local txt = doc.txt;
139         local c, w;
141         /* if col is NULL, set it to be the x cursor */
142         if(col == NULL) col = txt.x;
144         /* gets line where cursor is */
145         c = txt.lines[txt.y];
147         /* deletes from col to the end of line */
148         w = splice(c, NULL, col, -1);
150         /* set first part as current line */
151         txt.lines[txt.y] = w[0];
153         /* move to next line */
154         txt.y++;
156         /* insert a new line here */
157         expand(txt.lines, txt.y, 1);
159         /* fix the x cursor position */
160         txt.x -= col;
162         /* if autoindenting... */
163         if(mp.config.auto_indent)
164         {
165                 /* extract leading blanks in the original line
166                    to prepend them to the line to be inserted */
167                 local i = regex("/^[ \t]*[-\+\*]?[ \t]+/", c, 0);
169                 /* substitute all non-tab characters with spaces */
170                 i = sregex("/[^\t]/g", i, " ");
172                 /* delete any blank in the new line */
173                 w[1] = sregex("/^[ \t]*/", w[1]);
175                 /* concatenate */
176                 w[1] = i ~ w[1];
178                 /* the x position is further the length of that */
179                 txt.x += size(i);
180         }
182         /* put second part there (or an empty string if NULL) */
183         txt.lines[txt.y] = w[1] || '';
185         txt.mod++;
189 sub mp.join_line(doc)
190 /* joins the current line with the next one */
192         local txt = doc.txt;
194         if(txt.y < size(txt.lines))
195         {
196                 /* concats current line with the next one */
197                 txt.lines[txt.y] = txt.lines[txt.y] ~ txt.lines[txt.y + 1];
199                 /* delete it */
200                 adel(txt.lines, txt.y + 1);
202                 txt.mod++;
203         }
207 sub mp.delete_line(doc)
208 /* deletes the current line */
210         local txt = doc.txt;
211         local vx;
213         /* take current position */
214         vx = mp.x2vx(txt.lines[txt.y], txt.x);
216         /* if it's the only line, just replace it */
217         if(size(txt.lines) == 1)
218                 txt.lines[0] = '';
219         else
220         {
221                 /* destroy the line */
222                 adel(txt.lines, txt.y);
223         }
225         /* fix if it was the last line */
226         if(txt.y >= size(txt.lines))
227                 txt.y = size(txt.lines) - 1;
229         /* move to previous x position */
230         txt.x = mp.vx2x(txt.lines[txt.y], vx);
232         txt.mod++;
236 sub mp.delete_char(doc)
237 /* deletes the current char */
239         local txt = doc.txt;
241         /* is it over the end of line? */
242         if(txt.x == size(txt.lines[txt.y]))
243                 mp.join_line(doc);
244         else
245         {
246                 local w;
248                 w = splice(txt.lines[txt.y], NULL, txt.x, 1);
249                 txt.lines[txt.y] = w[0];
250         }
252         txt.mod++;
256 sub mp.delete_range(doc, bx, by, ex, ey)
257 /* deletes a range of characters from a document */
259         local txt = doc.txt;
261         /* move to the start of the range */
262         txt.x = bx;
263         txt.y = by;
265         if(by == ey)
266         {
267                 local w;
269                 /* block is just one line; delete the middle part */
270                 w = splice(txt.lines[by], NULL, bx, ex - bx);
272                 txt.lines[by] = w[0];
273         }
274         else
275         {
276                 local w;
278                 /* block has more than one line */
280                 /* delete from the beginning to the end of the first line */
281                 w = splice(txt.lines[by], NULL, bx, -1);
282                 txt.lines[by] = w[0];
284                 /* delete from the beginning of the last line to
285                    the end of the block */
286                 w = splice(txt.lines[ey], NULL, 0, ex);
287                 txt.lines[ey] = w[0];
289                 /* collapse the lines in between */
290                 collapse(txt.lines, by + 1, ey - by - 1);
292                 /* finally join both lines */
293                 mp.join_line(doc);
294         }
296         txt.mod++;
300 sub mp.insert_string(doc, str)
301 /* inserts a string into the cursor position */
303         local txt = doc.txt;
304         local w;
306         /* splice and change */
307         w = splice(txt.lines[txt.y], str, txt.x, 0);
308         txt.lines[txt.y] = w[0];
310         /* move right */
311         txt.x += size(str);
313         txt.mod++;
317 sub mp.insert(doc, a)
318 /* inserts an array of text into a document */
320         local txt = doc.txt;
321         local s;
323         /* if a is not an array, split it */
324         if(!is_array(a))
325                 a = split("\n", a);
327         /* empty array? return */
328         if((s = size(a)) == 0) return;
330         /* paste first line into current position */
331         mp.insert_string(doc, a[0]);
333         /* more than just one line? */
334         if(s > 1)
335         {
336                 /* break current line in two */
337                 mp.break_line(doc);
339                 /* insert last line */
340                 mp.insert_string(doc, a[s - 1]);
341         }
343         /* more than two lines? */
344         if(s > 2)
345         {
346                 local n = 1;
348                 /* open room */
349                 expand(txt.lines, txt.y, s - 2);
351                 /* transfer middle lines */
352                 while(n < s - 1)
353                         txt.lines[txt.y++] = a[n++];
354         }
358 sub mp.wrap_words(doc)
359 /* do the word wrapping */
361         local txt = doc.txt;
363         if(mp.config.word_wrap == 0) return;
365         /* take the column where the cursor is */
366         local c = mp.x2vx(txt.lines[txt.y], txt.x);
368         if(c >= mp.config.word_wrap &&
369                 regex("/^.{1," ~ mp.config.word_wrap ~ "}\s/", txt.lines[txt.y]))
370         {
371                 local w;
373                 /* take the coordinates */
374                 w = regex();
376                 /* break the line there */
377                 mp.break_line(doc, w[1]);
379                 /* delete the space at the end of the line */
380                 txt.lines[txt.y - 1] = sregex("/\s$/", txt.lines[txt.y - 1], NULL);
381         }
385 sub mp.insert_space(doc)
386 /* inserts a space, taking wordwrapping into account */
388         local txt = doc.txt;
390         mp.wrap_words(doc);
391         mp.insert(doc, ' ');
395 sub mp.insert_tab(doc)
396 /* inserts a tab */
398         if(mp.config.tabs_as_spaces)
399         {
400                 /* number of spaces to insert */
401                 local n = mp.config.tab_size -
402                         ((doc.txt.x) % mp.config.tab_size);
404                 while(n--) mp.insert(doc, ' ');
405         }
406         else
407                 mp.insert(doc, "\t");
411 sub mp.insert_newline(doc)
412 /* inserts a newline */
414         mp.wrap_words(doc);
415         mp.break_line(doc);
419 sub mp.insert_keystroke(doc, key)
420 /* inserts from a keystroke (with undo) */
422         mp.store_undo(doc);
423         mp.insert(doc, key);
427 /* undo */
429 sub mp.store_undo(doc)
430 /* stores the current txt in the undo queue */
432         queue(doc.undo, clone(doc.txt), mp.config.undo_levels);
433         doc.redo = [];
437 sub mp.undo(doc)
438 /* undoes last operation */
440         local txt;
442         if(txt = pop(doc.undo))
443         {
444                 queue(doc.redo, clone(doc.txt), mp.config.undo_levels);
445                 doc.txt = txt;
446         }
450 sub mp.redo(doc)
451 /* redoes last undid operation */
453         local txt;
455         if(txt = pop(doc.redo))
456         {
457                 queue(doc.undo, clone(doc.txt), mp.config.undo_levels);
458                 doc.txt = txt;
459         }
462 /* paragraphs */
464 sub mp.join_paragraph(doc)
465 /* joins current paragraph in just one line */
467         local txt = doc.txt;
468         local l;
470         while((l = txt.lines[txt.y + 1]) && size(l))
471         {
472                 /* delete all leading blanks in the next line */
473                 txt.lines[txt.y + 1] = sregex("/^[ \t]+/", txt.lines[txt.y + 1]);
475                 /* move to end of line and add a space separator */
476                 mp.move_eol(doc);
477                 mp.insert(doc, ' ');
479                 /* really join */
480                 mp.join_line(doc);
481         }
485 sub mp.word_wrap_paragraph(doc)
486 /* word wraps current paragraph */
488         local txt = doc.txt;
490         if(mp.config.word_wrap == 0)
491                 return;
493         mp.join_paragraph(doc);
495         mp.move_eol(doc);
497         while(size(txt.lines[txt.y]) > mp.config.word_wrap)
498         {
499                 mp.insert_space(doc);
500                 mp.move_left(doc);
501                 mp.delete_char(doc);
502         }