Updated language files.
[mp-5.x.git] / mp_edit.mpsl
blob00ec20735039cf5b28892e743bd1b60b3fbc9fd7
1 /*
3     Minimum Profit 5.x
4     A Programmer's Text Editor
6     Editing.
8     Copyright (C) 1991-2009 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); };
43 mp.actions['delete_left']       = sub (d) {
44         if (d.txt.x + d.txt.y) {
45                 mp.store_undo(d);
46                 mp.move_left(d);
47                 mp.delete_char(d);
48         }
51 mp.actions['undo']              = sub (d) { mp.undo(d); };
52 mp.actions['redo']              = sub (d) { mp.redo(d); };
53 mp.actions['join_paragraph']    = sub (d) { mp.store_undo(d); mp.join_paragraph(d); };
55 mp.actions['word_wrap_paragraph'] = sub (d) {
57         if(mp.config.word_wrap == 0)
58                 mp.alert(L("Word wrapping must be set"));
59         else {
60                 mp.store_undo(d);
61                 mp.word_wrap_paragraph(d);
62         }
65 mp.actions['line_options']      = sub (d) {
67         /* convert special characters on end of line */
68         local lt = mp.backslash_codes(mp.config.eol, 1);
70         local t = mp.form( [
71                 { 'label'       => L("Word wrap on column (0, no word wrap):"),
72                   'type'        => 'text',
73                   'value'       => mp.config.word_wrap,
74                   'history'     => 'wordwrap' },
75                 { 'label'       => L("Automatic indentation") ~ ':',
76                   'type'        => 'checkbox',
77                   'value'       => mp.config.auto_indent },
78                 { 'label'       => L("Line termination") ~ ':',
79                   'value'       => lt,
80                   'type'        => 'text' },
81                 { 'label'       => L("Keep original end of lines") ~ ':',
82                   'value'       => mp.config.keep_eol,
83                   'type'        => 'checkbox' },
84                 { 'label'       => L("Mark end of lines") ~ ':',
85                   'value'       => mp.config.mark_eol,
86                   'type'        => 'checkbox' }
87         ] );
89         if (t != NULL) {
90                 mp.config.word_wrap = t[0];
91                 mp.config.auto_indent = t[1];
92                 mp.config.eol = mp.backslash_codes(t[2], 0);
93                 mp.config.keep_eol = t[3];
94                 mp.config.mark_eol = t[4];
95         }
98 mp.actions['tab_options']       = sub (d) {
100         local t = mp.form( [
101                 { 'label'       => L("Tab size") ~ ':',
102                   'type'        => 'text',
103                   'value'       => mp.config.tab_size,
104                   'history'     => 'tabsize' },
105                 { 'label'       => L("Convert tabs to spaces") ~ ':',
106                   'type'        => 'checkbox',
107                   'value'       => mp.config.tabs_as_spaces }
108         ] );
110         if (t != NULL) {
111                 mp.config.tab_size = t[0];
112                 mp.config.tabs_as_spaces = t[1];
113         }
116 /** default key bindings **/
118 mp.keycodes['enter']    = "insert_line";
119 mp.keycodes['tab']      = "insert_tab";
120 mp.keycodes['space']    = "insert_space";
121 mp.keycodes['delete']   = "delete";
122 mp.keycodes['backspace']= "delete_left";
123 mp.keycodes['ctrl-i']   = "insert_tab";
124 mp.keycodes['ctrl-m']   = "insert_line";
125 mp.keycodes['ctrl-y']   = "delete_line";
126 mp.keycodes['ctrl-z']   = "undo";
127 mp.keycodes['f4']       = "word_wrap_paragraph";
129 /** action descriptions **/
131 mp.actdesc['insert_line']       = LL("Insert line");
132 mp.actdesc['delete_line']       = LL("Delete line");
133 mp.actdesc['insert_space']      = LL("Insert space");
134 mp.actdesc['insert_tab']        = LL("Insert tab");
135 mp.actdesc['delete']            = LL("Delete character");
136 mp.actdesc['delete_left']       = LL("Delete character to the left");
137 mp.actdesc['undo']              = LL("Undo");
138 mp.actdesc['redo']              = LL("Redo");
139 mp.actdesc['join_paragraph']    = LL("Join a paragraph in one line");
140 mp.actdesc['word_wrap_paragraph'] = LL("Word-wrap a paragraph");
141 mp.actdesc['line_options']      = LL("Line options...");
142 mp.actdesc['tab_options']       = LL("Tab options...");
144 /** code **/
147  * mp.break_line - Breaks current line in two (inserts a newline).
148  * @doc: the document
149  * @col: column where the newline will be inserted 
151  * Breaks current line in two by inserting a newline character in between.
152  * If @col is not NULL, the newline will be inserted in that column; otherwise,
153  * the current x position will be used.
154  */
155 sub mp.break_line(doc, col)
157         local txt = doc.txt;
158         local c, w;
160         /* if col is NULL, set it to be the x cursor */
161         if (col == NULL)
162                 col = txt.x;
164         /* gets line where cursor is */
165         c = txt.lines[txt.y];
167         /* deletes from col to the end of line */
168         w = splice(c, NULL, col, -1);
170         /* set first part as current line */
171         txt.lines[txt.y] = w[0];
173         /* move to next line */
174         txt.y++;
176         /* insert a new line here */
177         expand(txt.lines, txt.y, 1);
179         /* fix the x cursor position */
180         txt.x -= col;
182         /* if autoindenting... */
183         if (mp.config.auto_indent) {
184                 /* extract leading blanks in the original line
185                    to prepend them to the line to be inserted */
186                 local i = regex("/^[ \t]*[-\+\*]?[ \t]+/", c, 0);
188                 /* substitute all non-tab characters with spaces */
189                 i = sregex("/[^\t]/g", i, " ");
191                 /* delete any blank in the new line */
192                 w[1] = sregex("/^[ \t]*/", w[1]);
194                 /* concatenate */
195                 w[1] = i ~ w[1];
197                 /* the x position is further the length of that */
198                 txt.x += size(i);
199         }
201         /* put second part there (or an empty string if NULL) */
202         txt.lines[txt.y] = w[1] || '';
204         txt.mod++;
206         return doc;
210 sub mp.join_line(doc)
211 /* joins the current line with the next one */
213         local txt = doc.txt;
215         if (txt.y < size(txt.lines)) {
216                 /* concats current line with the next one */
217                 txt.lines[txt.y] = txt.lines[txt.y] ~ txt.lines[txt.y + 1];
219                 /* delete it */
220                 adel(txt.lines, txt.y + 1);
222                 txt.mod++;
223         }
225         return doc;
229 sub mp.delete_line(doc)
230 /* deletes the current line */
232         local txt = doc.txt;
233         local vx;
235         /* take current position */
236         vx = mp.x2vx(txt.lines[txt.y], txt.x);
238         /* if it's the only line, just replace it */
239         if (size(txt.lines) == 1)
240                 txt.lines[0] = '';
241         else {
242                 /* destroy the line */
243                 adel(txt.lines, txt.y);
244         }
246         /* fix if it was the last line */
247         if (txt.y >= size(txt.lines))
248                 txt.y = size(txt.lines) - 1;
250         /* move to previous x position */
251         txt.x = mp.vx2x(txt.lines[txt.y], vx);
253         txt.mod++;
255         return doc;
259 sub mp.delete_char(doc)
260 /* deletes the current char */
262         local txt = doc.txt;
264         /* is it over the end of line? */
265         if (txt.x == size(txt.lines[txt.y]))
266                 mp.join_line(doc);
267         else {
268                 local w;
270                 w = splice(txt.lines[txt.y], NULL, txt.x, 1);
271                 txt.lines[txt.y] = w[0];
272         }
274         txt.mod++;
276         return doc;
280 sub mp.delete_range(doc, bx, by, ex, ey, v)
281 /* deletes a range of characters from a document */
283         local txt = doc.txt;
285         /* move to the start of the range */
286         txt.x = bx;
287         txt.y = by;
289         if (by == ey) {
290                 local w;
292                 /* block is just one line; delete the middle part */
293                 w = splice(txt.lines[by], NULL, bx, ex - bx);
295                 txt.lines[by] = w[0];
296         }
297         else {
298                 /* block has more than one line */
299                 local w;
301                 if (v == 0) {
302                         /* delete using normal selection block */
304                         /* delete from the beginning to the end of the first line */
305                         w = splice(txt.lines[by], NULL, bx, -1);
306                         txt.lines[by] = w[0];
308                         /* delete from the beginning of the last line to
309                            the end of the block */
310                         w = splice(txt.lines[ey], NULL, 0, ex);
311                         txt.lines[ey] = w[0];
313                         /* collapse the lines in between */
314                         collapse(txt.lines, by + 1, ey - by - 1);
316                         /* finally join both lines */
317                         mp.join_line(doc);
318                 }
319                 else {
320                         /* delete using vertical selection block */
321                         while (by <= ey) {
322                                 w = splice(txt.lines[by], NULL, bx, ex - bx);
323                                 txt.lines[by] = w[0];
324                                 by++;
325                         }
326                 }
327         }
329         txt.mod++;
331         return doc;
335 sub mp.insert_string(doc, str)
336 /* inserts a string into the cursor position */
338         local txt = doc.txt;
339         local w;
341         /* splice and change */
342         w = splice(txt.lines[txt.y], str, txt.x, 0);
343         txt.lines[txt.y] = w[0];
345         /* move right */
346         txt.x += size(str);
348         txt.mod++;
350         return doc;
354 sub mp.insert(doc, a)
355 /* inserts an array of text into a document */
357         local txt = doc.txt;
358         local s;
360         /* if a is not an array, split it */
361         if (!is_array(a))
362                 a = split("\n", a);
364         /* empty array? return */
365         if ((s = size(a)) == 0)
366                 return doc;
368         /* paste first line into current position */
369         mp.insert_string(doc, a[0]);
371         /* more than just one line? */
372         if (s > 1) {
373                 /* break current line in two */
374                 mp.break_line(doc);
376                 /* insert last line */
377                 mp.insert_string(doc, a[s - 1]);
378         }
380         /* more than two lines? */
381         if (s > 2) {
382                 local n = 1;
384                 /* open room */
385                 expand(txt.lines, txt.y, s - 2);
387                 /* transfer middle lines */
388                 while (n < s - 1)
389                         txt.lines[txt.y++] = a[n++];
390         }
392         return doc;
396 sub mp.wrap_words(doc)
397 /* do the word wrapping */
399         local txt = doc.txt;
401         if (mp.config.word_wrap == 0)
402                 return doc;
404         /* take the column where the cursor is */
405         local c = mp.x2vx(txt.lines[txt.y], txt.x);
407         if (c >= mp.config.word_wrap &&
408                 regex("/^.{1," ~ mp.config.word_wrap ~ "}[ \t]/", txt.lines[txt.y])) {
409                 local w;
411                 /* take the coordinates */
412                 w = regex();
414                 /* break the line there */
415                 mp.break_line(doc, w[1]);
417                 /* delete the space at the end of the line */
418                 txt.lines[txt.y - 1] = sregex("/[ \t]$/", txt.lines[txt.y - 1], NULL);
419         }
421         return doc;
425 sub mp.insert_space(doc)
426 /* inserts a space, taking wordwrapping into account */
428         mp.wrap_words(doc);
429         mp.insert(doc, ' ');
433 sub mp.insert_tab(doc)
434 /* inserts a tab */
436         if (mp.config.tabs_as_spaces) {
437                 /* number of spaces to insert */
438                 local n = mp.config.tab_size -
439                         ((doc.txt.x) % mp.config.tab_size);
441                 while(n--) mp.insert(doc, ' ');
442         }
443         else
444                 mp.insert(doc, "\t");
446         return doc;
450 sub mp.insert_newline(doc)
451 /* inserts a newline */
453         mp.wrap_words(doc);
454         mp.break_line(doc);
458 sub mp.insert_keystroke(doc, key)
459 /* inserts from a keystroke (with undo) */
461         if (size(key) == 1) {
462                 mp.store_undo(doc);
464                 mp.insert(doc, key);
465         }
466         else
467         if (key != NULL) {
468                 mp.message = {
469                         'timeout'       => time() + 2,
470                         'string'        => sprintf(L("Unbound keystroke '%s'"), key)
471                 };
472         }
474         return doc;
478 /** undo **/
480 sub mp.store_undo(doc)
481 /* stores the current txt in the undo queue */
483         queue(doc.undo, clone(doc.txt), mp.config.undo_levels);
484         doc.redo = [];
486         return doc;
490 sub mp.undo(doc)
491 /* undoes last operation */
493         local txt;
495         if (txt = pop(doc.undo)) {
496                 queue(doc.redo, clone(doc.txt), mp.config.undo_levels);
497                 doc.txt = txt;
498         }
500         return doc;
504 sub mp.redo(doc)
505 /* redoes last undid operation */
507         local txt;
509         if (txt = pop(doc.redo)) {
510                 queue(doc.undo, clone(doc.txt), mp.config.undo_levels);
511                 doc.txt = txt;
512         }
514         return doc;
518 /** paragraphs **/
520 sub mp.join_paragraph(doc)
521 /* joins current paragraph in just one line */
523         local txt = doc.txt;
524         local l;
526         while ((l = txt.lines[txt.y + 1]) && size(l)) {
527                 /* delete all leading blanks in the next line */
528                 txt.lines[txt.y + 1] = sregex("/^[ \t]+/", txt.lines[txt.y + 1]);
530                 /* move to end of line and add a space separator */
531                 mp.move_eol(doc);
532                 mp.insert(doc, ' ');
534                 /* really join */
535                 mp.join_line(doc);
536         }
538         return doc;
542 sub mp.word_wrap_paragraph(doc)
543 /* word wraps current paragraph */
545         local txt = doc.txt;
547         if (mp.config.word_wrap == 0)
548                 return doc;
550         mp.join_paragraph(doc);
552         mp.move_eol(doc);
554         while (size(txt.lines[txt.y]) > mp.config.word_wrap) {
555                 mp.insert_space(doc);
556                 mp.move_left(doc);
557                 mp.delete_char(doc);
558         }
560         return doc;