Version 5.0.0 RELEASED.
[mp-5.x.git] / mp_edit.mpsl
blob819814be469f2a44f43eb5b333976561a3cb833d
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                 mp.store_undo(d);
54                 mp.word_wrap_paragraph(d);
55         }
58 mp.actions['line_options']      = sub (d) {
60         /* convert special characters on end of line */
61         local lt = mp.backslash_codes(mp.config.eol, 1);
63         local t = mp.form( [
64                 { 'label'       => L("Word wrap on column (0, no word wrap):"),
65                   'type'        => 'text',
66                   'value'       => mp.config.word_wrap,
67                   'history'     => 'wordwrap' },
68                 { 'label'       => L("Automatic indentation") ~ ':',
69                   'type'        => 'checkbox',
70                   'value'       => mp.config.auto_indent },
71                 { 'label'       => L("Line termination") ~ ':',
72                   'value'       => lt,
73                   'type'        => 'text' }
74         ] );
76         if (t != NULL) {
77                 mp.config.word_wrap = t[0];
78                 mp.config.auto_indent = t[1];
79                 mp.config.eol = mp.backslash_codes(t[2], 0);
80         }
83 mp.actions['tab_options']       = sub (d) {
85         local t = mp.form( [
86                 { 'label'       => L("Tab size") ~ ':',
87                   'type'        => 'text',
88                   'value'       => mp.config.tab_size,
89                   'history'     => 'tabsize' },
90                 { 'label'       => L("Convert tabs to spaces") ~ ':',
91                   'type'        => 'checkbox',
92                   'value'       => mp.config.tabs_as_spaces }
93         ] );
95         if (t != NULL) {
96                 mp.config.tab_size = t[0];
97                 mp.config.tabs_as_spaces = t[1];
98         }
101 /* default key bindings */
103 mp.keycodes['enter']    = "insert_line";
104 mp.keycodes['tab']      = "insert_tab";
105 mp.keycodes['space']    = "insert_space";
106 mp.keycodes['delete']   = "delete";
107 mp.keycodes['backspace']= "delete_left";
108 mp.keycodes['ctrl-i']   = "insert_tab";
109 mp.keycodes['ctrl-m']   = "insert_line";
110 mp.keycodes['ctrl-y']   = "delete_line";
111 mp.keycodes['ctrl-z']   = "undo";
112 mp.keycodes['f4']       = "word_wrap_paragraph";
113 mp.keycodes['f6']       = "join_paragraph";
115 /* action descriptions */
117 mp.actdesc['insert_line']       = LL("Insert line");
118 mp.actdesc['delete_line']       = LL("Delete line");
119 mp.actdesc['insert_space']      = LL("Insert space");
120 mp.actdesc['insert_tab']        = LL("Insert tab");
121 mp.actdesc['delete']            = LL("Delete character");
122 mp.actdesc['delete_left']       = LL("Delete character to the left");
123 mp.actdesc['undo']              = LL("Undo");
124 mp.actdesc['redo']              = LL("Redo");
125 mp.actdesc['join_paragraph']    = LL("Join a paragraph in one line");
126 mp.actdesc['word_wrap_paragraph'] = LL("Word-wrap a paragraph");
127 mp.actdesc['line_options']      = LL("Line options...");
128 mp.actdesc['tab_options']       = LL("Tab options...");
130 /* code */
132 sub mp.break_line(doc, col)
133 /* breaks current line in two (inserts a newline) */
135         local txt = doc.txt;
136         local c, w;
138         /* if col is NULL, set it to be the x cursor */
139         if (col == NULL)
140                 col = txt.x;
142         /* gets line where cursor is */
143         c = txt.lines[txt.y];
145         /* deletes from col to the end of line */
146         w = splice(c, NULL, col, -1);
148         /* set first part as current line */
149         txt.lines[txt.y] = w[0];
151         /* move to next line */
152         txt.y++;
154         /* insert a new line here */
155         expand(txt.lines, txt.y, 1);
157         /* fix the x cursor position */
158         txt.x -= col;
160         /* if autoindenting... */
161         if (mp.config.auto_indent) {
162                 /* extract leading blanks in the original line
163                    to prepend them to the line to be inserted */
164                 local i = regex("/^[ \t]*[-\+\*]?[ \t]+/", c, 0);
166                 /* substitute all non-tab characters with spaces */
167                 i = sregex("/[^\t]/g", i, " ");
169                 /* delete any blank in the new line */
170                 w[1] = sregex("/^[ \t]*/", w[1]);
172                 /* concatenate */
173                 w[1] = i ~ w[1];
175                 /* the x position is further the length of that */
176                 txt.x += size(i);
177         }
179         /* put second part there (or an empty string if NULL) */
180         txt.lines[txt.y] = w[1] || '';
182         txt.mod++;
186 sub mp.join_line(doc)
187 /* joins the current line with the next one */
189         local txt = doc.txt;
191         if (txt.y < size(txt.lines)) {
192                 /* concats current line with the next one */
193                 txt.lines[txt.y] = txt.lines[txt.y] ~ txt.lines[txt.y + 1];
195                 /* delete it */
196                 adel(txt.lines, txt.y + 1);
198                 txt.mod++;
199         }
203 sub mp.delete_line(doc)
204 /* deletes the current line */
206         local txt = doc.txt;
207         local vx;
209         /* take current position */
210         vx = mp.x2vx(txt.lines[txt.y], txt.x);
212         /* if it's the only line, just replace it */
213         if (size(txt.lines) == 1)
214                 txt.lines[0] = '';
215         else {
216                 /* destroy the line */
217                 adel(txt.lines, txt.y);
218         }
220         /* fix if it was the last line */
221         if (txt.y >= size(txt.lines))
222                 txt.y = size(txt.lines) - 1;
224         /* move to previous x position */
225         txt.x = mp.vx2x(txt.lines[txt.y], vx);
227         txt.mod++;
231 sub mp.delete_char(doc)
232 /* deletes the current char */
234         local txt = doc.txt;
236         /* is it over the end of line? */
237         if (txt.x == size(txt.lines[txt.y]))
238                 mp.join_line(doc);
239         else {
240                 local w;
242                 w = splice(txt.lines[txt.y], NULL, txt.x, 1);
243                 txt.lines[txt.y] = w[0];
244         }
246         txt.mod++;
250 sub mp.delete_range(doc, bx, by, ex, ey)
251 /* deletes a range of characters from a document */
253         local txt = doc.txt;
255         /* move to the start of the range */
256         txt.x = bx;
257         txt.y = by;
259         if (by == ey) {
260                 local w;
262                 /* block is just one line; delete the middle part */
263                 w = splice(txt.lines[by], NULL, bx, ex - bx);
265                 txt.lines[by] = w[0];
266         }
267         else {
268                 local w;
270                 /* block has more than one line */
272                 /* delete from the beginning to the end of the first line */
273                 w = splice(txt.lines[by], NULL, bx, -1);
274                 txt.lines[by] = w[0];
276                 /* delete from the beginning of the last line to
277                    the end of the block */
278                 w = splice(txt.lines[ey], NULL, 0, ex);
279                 txt.lines[ey] = w[0];
281                 /* collapse the lines in between */
282                 collapse(txt.lines, by + 1, ey - by - 1);
284                 /* finally join both lines */
285                 mp.join_line(doc);
286         }
288         txt.mod++;
292 sub mp.insert_string(doc, str)
293 /* inserts a string into the cursor position */
295         local txt = doc.txt;
296         local w;
298         /* splice and change */
299         w = splice(txt.lines[txt.y], str, txt.x, 0);
300         txt.lines[txt.y] = w[0];
302         /* move right */
303         txt.x += size(str);
305         txt.mod++;
309 sub mp.insert(doc, a)
310 /* inserts an array of text into a document */
312         local txt = doc.txt;
313         local s;
315         /* if a is not an array, split it */
316         if (!is_array(a))
317                 a = split("\n", a);
319         /* empty array? return */
320         if ((s = size(a)) == 0)
321                 return;
323         /* paste first line into current position */
324         mp.insert_string(doc, a[0]);
326         /* more than just one line? */
327         if (s > 1) {
328                 /* break current line in two */
329                 mp.break_line(doc);
331                 /* insert last line */
332                 mp.insert_string(doc, a[s - 1]);
333         }
335         /* more than two lines? */
336         if (s > 2) {
337                 local n = 1;
339                 /* open room */
340                 expand(txt.lines, txt.y, s - 2);
342                 /* transfer middle lines */
343                 while (n < s - 1)
344                         txt.lines[txt.y++] = a[n++];
345         }
349 sub mp.wrap_words(doc)
350 /* do the word wrapping */
352         local txt = doc.txt;
354         if (mp.config.word_wrap == 0)
355                 return;
357         /* take the column where the cursor is */
358         local c = mp.x2vx(txt.lines[txt.y], txt.x);
360         if (c >= mp.config.word_wrap &&
361                 regex("/^.{1," ~ mp.config.word_wrap ~ "}\s/", txt.lines[txt.y])) {
362                 local w;
364                 /* take the coordinates */
365                 w = regex();
367                 /* break the line there */
368                 mp.break_line(doc, w[1]);
370                 /* delete the space at the end of the line */
371                 txt.lines[txt.y - 1] = sregex("/\s$/", txt.lines[txt.y - 1], NULL);
372         }
376 sub mp.insert_space(doc)
377 /* inserts a space, taking wordwrapping into account */
379         local txt = doc.txt;
381         mp.wrap_words(doc);
382         mp.insert(doc, ' ');
386 sub mp.insert_tab(doc)
387 /* inserts a tab */
389         if (mp.config.tabs_as_spaces) {
390                 /* number of spaces to insert */
391                 local n = mp.config.tab_size -
392                         ((doc.txt.x) % mp.config.tab_size);
394                 while(n--) mp.insert(doc, ' ');
395         }
396         else
397                 mp.insert(doc, "\t");
401 sub mp.insert_newline(doc)
402 /* inserts a newline */
404         mp.wrap_words(doc);
405         mp.break_line(doc);
409 sub mp.insert_keystroke(doc, key)
410 /* inserts from a keystroke (with undo) */
412         mp.store_undo(doc);
414         if (size(key) == 1)
415                 mp.insert(doc, key);
416         else
417         if (key != NULL) {
418                 mp.message = {
419                         'timeout'       => time() + 2,
420                         'string'        => sprintf(L("Unbound keystroke '%s'"), key)
421                 };
422         }
426 /* undo */
428 sub mp.store_undo(doc)
429 /* stores the current txt in the undo queue */
431         queue(doc.undo, clone(doc.txt), mp.config.undo_levels);
432         doc.redo = [];
436 sub mp.undo(doc)
437 /* undoes last operation */
439         local txt;
441         if (txt = pop(doc.undo)) {
442                 queue(doc.redo, clone(doc.txt), mp.config.undo_levels);
443                 doc.txt = txt;
444         }
448 sub mp.redo(doc)
449 /* redoes last undid operation */
451         local txt;
453         if (txt = pop(doc.redo)) {
454                 queue(doc.undo, clone(doc.txt), mp.config.undo_levels);
455                 doc.txt = txt;
456         }
459 /* paragraphs */
461 sub mp.join_paragraph(doc)
462 /* joins current paragraph in just one line */
464         local txt = doc.txt;
465         local l;
467         while ((l = txt.lines[txt.y + 1]) && size(l)) {
468                 /* delete all leading blanks in the next line */
469                 txt.lines[txt.y + 1] = sregex("/^[ \t]+/", txt.lines[txt.y + 1]);
471                 /* move to end of line and add a space separator */
472                 mp.move_eol(doc);
473                 mp.insert(doc, ' ');
475                 /* really join */
476                 mp.join_line(doc);
477         }
481 sub mp.word_wrap_paragraph(doc)
482 /* word wraps current paragraph */
484         local txt = doc.txt;
486         if (mp.config.word_wrap == 0)
487                 return;
489         mp.join_paragraph(doc);
491         mp.move_eol(doc);
493         while (size(txt.lines[txt.y]) > mp.config.word_wrap) {
494                 mp.insert_space(doc);
495                 mp.move_left(doc);
496                 mp.delete_char(doc);
497         }