4 A Programmer's Text Editor
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
31 mp.actions['insert_line'] = sub (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) &&
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"));
54 mp.word_wrap_paragraph(d);
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);
64 { 'label' => L("Word wrap on column (0, no word wrap):"),
66 'value' => mp.config.word_wrap,
67 'history' => 'wordwrap' },
68 { 'label' => L("Automatic indentation") ~ ':',
70 'value' => mp.config.auto_indent },
71 { 'label' => L("Line termination") ~ ':',
77 mp.config.word_wrap = t[0];
78 mp.config.auto_indent = t[1];
79 mp.config.eol = mp.backslash_codes(t[2], 0);
83 mp.actions['tab_options'] = sub (d) {
86 { 'label' => L("Tab size") ~ ':',
88 'value' => mp.config.tab_size,
89 'history' => 'tabsize' },
90 { 'label' => L("Convert tabs to spaces") ~ ':',
92 'value' => mp.config.tabs_as_spaces }
96 mp.config.tab_size = t[0];
97 mp.config.tabs_as_spaces = t[1];
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...");
132 sub mp.break_line(doc, col)
133 /* breaks current line in two (inserts a newline) */
138 /* if col is NULL, set it to be the x cursor */
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 */
154 /* insert a new line here */
155 expand(txt.lines, txt.y, 1);
157 /* fix the x cursor position */
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]);
175 /* the x position is further the length of that */
179 /* put second part there (or an empty string if NULL) */
180 txt.lines[txt.y] = w[1] || '';
186 sub mp.join_line(doc)
187 /* joins the current line with the next one */
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];
196 adel(txt.lines, txt.y + 1);
203 sub mp.delete_line(doc)
204 /* deletes the current line */
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)
216 /* destroy the line */
217 adel(txt.lines, txt.y);
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);
231 sub mp.delete_char(doc)
232 /* deletes the current char */
236 /* is it over the end of line? */
237 if (txt.x == size(txt.lines[txt.y]))
242 w = splice(txt.lines[txt.y], NULL, txt.x, 1);
243 txt.lines[txt.y] = w[0];
250 sub mp.delete_range(doc, bx, by, ex, ey)
251 /* deletes a range of characters from a document */
255 /* move to the start of the range */
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];
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 */
292 sub mp.insert_string(doc, str)
293 /* inserts a string into the cursor position */
298 /* splice and change */
299 w = splice(txt.lines[txt.y], str, txt.x, 0);
300 txt.lines[txt.y] = w[0];
309 sub mp.insert(doc, a)
310 /* inserts an array of text into a document */
315 /* if a is not an array, split it */
319 /* empty array? return */
320 if ((s = size(a)) == 0)
323 /* paste first line into current position */
324 mp.insert_string(doc, a[0]);
326 /* more than just one line? */
328 /* break current line in two */
331 /* insert last line */
332 mp.insert_string(doc, a[s - 1]);
335 /* more than two lines? */
340 expand(txt.lines, txt.y, s - 2);
342 /* transfer middle lines */
344 txt.lines[txt.y++] = a[n++];
349 sub mp.wrap_words(doc)
350 /* do the word wrapping */
354 if (mp.config.word_wrap == 0)
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])) {
364 /* take the coordinates */
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);
376 sub mp.insert_space(doc)
377 /* inserts a space, taking wordwrapping into account */
386 sub mp.insert_tab(doc)
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, ' ');
397 mp.insert(doc, "\t");
401 sub mp.insert_newline(doc)
402 /* inserts a newline */
409 sub mp.insert_keystroke(doc, key)
410 /* inserts from a keystroke (with undo) */
419 'timeout' => time() + 2,
420 'string' => sprintf(L("Unbound keystroke '%s'"), key)
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);
437 /* undoes last operation */
441 if (txt = pop(doc.undo)) {
442 queue(doc.redo, clone(doc.txt), mp.config.undo_levels);
449 /* redoes last undid operation */
453 if (txt = pop(doc.redo)) {
454 queue(doc.undo, clone(doc.txt), mp.config.undo_levels);
461 sub mp.join_paragraph(doc)
462 /* joins current paragraph in just one line */
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 */
481 sub mp.word_wrap_paragraph(doc)
482 /* word wraps current paragraph */
486 if (mp.config.word_wrap == 0)
489 mp.join_paragraph(doc);
493 while (size(txt.lines[txt.y]) > mp.config.word_wrap) {
494 mp.insert_space(doc);