4 A Programmer's Text Editor
8 Copyright (C) 1991-2010 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) {
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) {
51 mp.actions['undo'] = sub (d) { mp.undo(d); };
52 mp.actions['redo'] = sub (d) { mp.redo(d); };
54 mp.actions['join_paragraph'] = sub (d) {
61 /* create a working document */
62 local p = mp.create('<wrk>', mp.clipboard);
64 /* while not at EOF, word wrap everything */
65 while (p.txt.y < size(p.txt.lines) - 1) {
71 /* insert the content */
72 mp.insert(d, p.txt.lines);
79 mp.actions['word_wrap_paragraph'] = sub (d) {
81 if(mp.config.word_wrap == 0)
82 mp.alert(L("Word wrapping must be set"));
90 /* create a working document */
91 local p = mp.create('<wrk>', mp.clipboard);
93 /* while not at EOF, word wrap everything */
94 while (p.txt.y < size(p.txt.lines) - 1) {
95 mp.word_wrap_paragraph(p);
100 /* insert the content */
101 mp.insert(d, p.txt.lines);
105 mp.word_wrap_paragraph(d);
109 mp.actions['line_options'] = sub (d) {
111 /* convert special characters on end of line */
112 local lt = mp.backslash_codes(mp.config.eol, 1);
115 { 'label' => L("Word wrap on column (0, no word wrap):"),
117 'value' => mp.config.word_wrap,
118 'history' => 'wordwrap' },
119 { 'label' => L("Automatic indentation") ~ ':',
120 'type' => 'checkbox',
121 'value' => mp.config.auto_indent },
122 { 'label' => L("Line termination") ~ ':',
125 { 'label' => L("Keep original end of lines") ~ ':',
126 'value' => mp.config.keep_eol,
127 'type' => 'checkbox' },
128 { 'label' => L("Mark end of lines") ~ ':',
129 'value' => mp.config.mark_eol,
130 'type' => 'checkbox' }
134 mp.config.word_wrap = t[0];
135 mp.config.auto_indent = t[1];
136 mp.config.eol = mp.backslash_codes(t[2], 0);
137 mp.config.keep_eol = t[3];
138 mp.config.mark_eol = t[4];
142 mp.actions['tab_options'] = sub (d) {
145 { 'label' => L("Tab size") ~ ':',
147 'value' => mp.config.tab_size,
148 'history' => 'tabsize' },
149 { 'label' => L("Convert tabs to spaces") ~ ':',
150 'type' => 'checkbox',
151 'value' => mp.config.tabs_as_spaces },
152 { 'label' => L("Use previous line for tab columns") ~ ':',
153 'type' => 'checkbox',
154 'value' => mp.config.dynamic_tabs }
158 mp.config.tab_size = t[0];
159 mp.config.tabs_as_spaces = t[1];
160 mp.config.dynamic_tabs = t[2];
164 mp.actions['toggle_insert'] = sub (d) { mp.config.insert = !mp.config.insert; };
166 /** default key bindings **/
168 mp.keycodes['enter'] = "insert_line";
169 mp.keycodes['tab'] = "insert_tab";
170 mp.keycodes['space'] = "insert_space";
171 mp.keycodes['delete'] = "delete";
172 mp.keycodes['backspace']= "delete_left";
173 mp.keycodes['ctrl-i'] = "insert_tab";
174 mp.keycodes['ctrl-m'] = "insert_line";
175 mp.keycodes['ctrl-y'] = "delete_line";
176 mp.keycodes['ctrl-z'] = "undo";
177 mp.keycodes['f4'] = "word_wrap_paragraph";
178 mp.keycodes['insert'] = "toggle_insert";
180 /** action descriptions **/
182 mp.actdesc['insert_line'] = LL("Insert line");
183 mp.actdesc['delete_line'] = LL("Delete line");
184 mp.actdesc['insert_space'] = LL("Insert space");
185 mp.actdesc['insert_tab'] = LL("Insert tab");
186 mp.actdesc['delete'] = LL("Delete character");
187 mp.actdesc['delete_left'] = LL("Delete character to the left");
188 mp.actdesc['undo'] = LL("Undo");
189 mp.actdesc['redo'] = LL("Redo");
190 mp.actdesc['join_paragraph'] = LL("Join a paragraph in one line");
191 mp.actdesc['word_wrap_paragraph'] = LL("Word-wrap a paragraph");
192 mp.actdesc['line_options'] = LL("Line options...");
193 mp.actdesc['tab_options'] = LL("Tab options...");
194 mp.actdesc['toggle_insert'] = LL("Toggle insert/overwrite mode");
199 * mp.break_line - Breaks current line in two (inserts a newline).
201 * @col: column where the newline will be inserted
203 * Breaks current line in two by inserting a newline character in between.
204 * If @col is not NULL, the newline will be inserted in that column; otherwise,
205 * the current x position will be used.
207 sub mp.break_line(doc, col)
212 /* if col is NULL, set it to be the x cursor */
216 /* gets line where cursor is */
217 c = txt.lines[txt.y];
219 /* deletes from col to the end of line */
220 w = splice(c, NULL, col, -1);
222 /* set first part as current line */
223 txt.lines[txt.y] = w[0];
225 /* move to next line */
228 /* insert a new line here */
229 expand(txt.lines, txt.y, 1);
231 /* fix the x cursor position */
234 /* if autoindenting... */
235 if (mp.config.auto_indent) {
236 /* extract leading blanks in the original line
237 to prepend them to the line to be inserted */
238 local i = regex("/^[ \t]*[-\+\*]?[ \t]+/", c, 0);
240 /* substitute all non-tab characters with spaces */
241 i = sregex("/[^\t]/g", i, " ");
243 /* delete any blank in the new line */
244 w[1] = sregex("/^[ \t]*/", w[1]);
249 /* the x position is further the length of that */
253 /* put second part there (or an empty string if NULL) */
254 txt.lines[txt.y] = w[1] || '';
262 sub mp.join_line(doc)
263 /* joins the current line with the next one */
267 if (txt.y < size(txt.lines)) {
268 /* concats current line with the next one */
269 txt.lines[txt.y] = txt.lines[txt.y] ~ txt.lines[txt.y + 1];
272 adel(txt.lines, txt.y + 1);
281 sub mp.delete_line(doc)
282 /* deletes the current line */
287 /* take current position */
288 vx = mp.x2vx(txt.lines[txt.y], txt.x);
290 /* if it's the only line, just replace it */
291 if (size(txt.lines) == 1)
294 /* destroy the line */
295 adel(txt.lines, txt.y);
298 /* fix if it was the last line */
299 if (txt.y >= size(txt.lines))
300 txt.y = size(txt.lines) - 1;
302 /* move to previous x position */
303 txt.x = mp.vx2x(txt.lines[txt.y], vx);
311 sub mp.delete_char(doc)
312 /* deletes the current char */
316 if (txt.mark != NULL) {
321 /* is it over the end of line? */
322 if (txt.x == size(txt.lines[txt.y]))
327 w = splice(txt.lines[txt.y], NULL, txt.x, 1);
328 txt.lines[txt.y] = w[0];
337 sub mp.delete_range(doc, bx, by, ex, ey, v)
338 /* deletes a range of characters from a document */
342 /* move to the start of the range */
349 /* block is just one line; delete the middle part */
350 w = splice(txt.lines[by], NULL, bx, ex - bx);
352 txt.lines[by] = w[0];
355 /* block has more than one line */
359 /* delete using normal selection block */
361 /* delete from the beginning to the end of the first line */
362 w = splice(txt.lines[by], NULL, bx, -1);
363 txt.lines[by] = w[0];
365 /* delete from the beginning of the last line to
366 the end of the block */
367 w = splice(txt.lines[ey], NULL, 0, ex);
368 txt.lines[ey] = w[0];
370 /* collapse the lines in between */
371 collapse(txt.lines, by + 1, ey - by - 1);
373 /* finally join both lines */
377 /* delete using vertical selection block */
379 w = splice(txt.lines[by], NULL, bx, ex - bx);
380 txt.lines[by] = w[0];
392 sub mp.insert_string(doc, str)
393 /* inserts a string into the cursor position */
400 /* splice and change */
401 w = splice(txt.lines[txt.y], str, txt.x, mp.config.insert && size(str) || 0);
402 txt.lines[txt.y] = w[0];
413 sub mp.insert(doc, a)
414 /* inserts an array of text into a document */
419 /* if a is not an array, split it */
423 /* empty array? return */
424 if ((s = size(a)) == 0)
427 /* paste first line into current position */
428 mp.insert_string(doc, a[0]);
430 /* more than just one line? */
432 /* break current line in two */
435 /* insert last line */
436 mp.insert_string(doc, a[s - 1]);
439 /* more than two lines? */
444 expand(txt.lines, txt.y, s - 2);
446 /* transfer middle lines */
448 txt.lines[txt.y++] = a[n++];
455 sub mp.wrap_words(doc)
456 /* do the word wrapping */
460 if (mp.config.word_wrap == 0)
463 /* take the column where the cursor is */
464 local c = mp.x2vx(txt.lines[txt.y], txt.x);
466 if (c >= mp.config.word_wrap &&
467 regex("/^.{1," ~ mp.config.word_wrap ~ "}[ \t]/", txt.lines[txt.y])) {
470 /* take the coordinates */
473 /* break the line there */
474 mp.break_line(doc, w[1]);
476 /* delete the space at the end of the line */
477 txt.lines[txt.y - 1] = sregex("/[ \t]$/", txt.lines[txt.y - 1], NULL);
484 sub mp.insert_space(doc)
485 /* inserts a space, taking wordwrapping into account */
492 sub mp.insert_tab(doc)
495 if (doc.txt.y && mp.config.dynamic_tabs) {
496 local pl = doc.txt.lines[doc.txt.y - 1];
498 if ((pl = regex("/[^ \t]*[ \t]+/", pl, doc.txt.x)) != NULL) {
499 pl = sregex("/[^\t]/g", pl, ' ');
505 if (mp.config.tabs_as_spaces) {
506 /* number of spaces to insert */
507 local n = mp.config.tab_size -
508 ((doc.txt.x) % mp.config.tab_size);
510 while(n--) mp.insert(doc, ' ');
513 mp.insert(doc, "\t");
519 sub mp.insert_newline(doc)
520 /* inserts a newline */
527 sub mp.insert_keystroke(doc, key)
528 /* inserts from a keystroke (with undo) */
530 if (size(key) == 1) {
537 'timeout' => time() + 2,
538 'string' => sprintf(L("Unbound keystroke '%s'"), key)
548 sub mp.store_undo(doc)
549 /* stores the current txt in the undo queue */
551 queue(doc.undo, clone(doc.txt), mp.config.undo_levels);
559 /* undoes last operation */
563 if (txt = pop(doc.undo)) {
564 queue(doc.redo, clone(doc.txt), mp.config.undo_levels);
573 /* redoes last undid operation */
577 if (txt = pop(doc.redo)) {
578 queue(doc.undo, clone(doc.txt), mp.config.undo_levels);
588 sub mp.join_paragraph(doc)
589 /* joins current paragraph in just one line */
594 while ((l = txt.lines[txt.y + 1]) && size(l)) {
595 /* delete all leading blanks in the next line */
596 txt.lines[txt.y + 1] = sregex("/^[ \t]+/", txt.lines[txt.y + 1]);
598 /* move to end of line and add a space separator */
610 sub mp.word_wrap_paragraph(doc)
611 /* word wraps current paragraph */
615 if (mp.config.word_wrap == 0)
618 mp.join_paragraph(doc);
622 while (size(txt.lines[txt.y]) > mp.config.word_wrap) {
623 mp.insert_space(doc);