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
28 /* successful searches will always be shown in this line */
29 mp.config.move_seek_to_line = 5;
33 mp.actions['seek'] = sub (d) {
35 { 'label' => L("Text to seek:"),
37 'history' => 'search' },
38 { 'label' => L("Case sensitive") ~ ':',
40 'value' => mp.config.case_sensitive_search }
45 mp.config.case_sensitive_search = t[1];
47 mp.long_op(mp.search, d, mp.backslash_codes(t[0])) ||
48 mp.alert(L("Text not found."));
52 mp.actions['seek_next'] = sub (d) { mp.long_op(mp.search, d, NULL) ||
53 mp.alert(L("Text not found."));
56 mp.actions['seek_prev'] = sub (d) { mp.long_op(mp.search_back, d, NULL) ||
57 mp.alert(L("Text not found."));
60 mp.actions['replace'] = sub (d) {
63 { 'label' => L("Replace text:"),
65 'history' => 'search'},
66 { 'label' => L("Replace with:"),
68 'history' => 'replace'},
69 { 'label' => L("Case sensitive") ~ ':',
71 'value' => mp.config.case_sensitive_search },
72 { 'label' => L("Global replace:"),
74 'value' => mp.config.global_replace }
79 mp.config.case_sensitive_search = r[2];
80 mp.config.global_replace = r[3];
83 mp.long_op(mp.replace, d, mp.backslash_codes(r[0]), mp.backslash_codes(r[1]));
87 mp.actions['seek_next_char'] = sub (d) { mp.seek_prev_or_next_char(d, mp.search); };
88 mp.actions['seek_prev_char'] = sub (d) { mp.seek_prev_or_next_char(d, mp.search_back); };
90 mp.actions['grep'] = sub (d) {
93 { 'label' => L("Text to seek:"),
95 'history' => 'search'},
96 { 'label' => L("Files to grep (empty, all):"),
101 if(r != NULL && r[0] ne '')
105 if((r = mp.long_op(mp.grep, '/' ~ r[0] ~ '/', r[1])) == NULL)
106 mp.alert(L("File(s) not found."));
109 mp.alert(L("Text not found."));
113 /* more than one; ask for it */
115 { 'label' => L("Grep"),
117 'list' => map(sub (e) {
118 sprintf("%s (%d): %s", e[0], e[1] + 1, e[2]); }, r)
125 /* get index of the desired item */
131 /* now open the document and move there */
132 d = mp.long_op(mp.open, r[i][0]);
134 mp.set_y(d, r[i][1]);
139 /* default key bindings */
141 mp.keycodes['f3'] = 'seek_next';
142 mp.keycodes['ctrl-f3'] = 'seek_prev';
143 mp.keycodes['ctrl-f'] = 'seek';
144 mp.keycodes['ctrl-r'] = 'replace';
145 mp.keycodes['ctrl-page-down'] = 'seek_next_char';
146 mp.keycodes['ctrl-page-up'] = 'seek_prev_char';
148 /* action descriptions */
149 mp.actdesc['seek'] = LL("Search text...");
150 mp.actdesc['seek_next'] = LL("Search next");
151 mp.actdesc['seek_prev'] = LL("Search previous");
152 mp.actdesc['replace'] = LL("Replace...");
153 mp.actdesc['seek_next_char'] = LL("Move to next instance of current char");
154 mp.actdesc['seek_prev_char'] = LL("Move to previous instance of current char");
155 mp.actdesc['grep'] = LL("Grep (find inside) files...");
159 sub mp.prefix_regex(str)
160 /* set str to be a valid regex */
162 /* surround with / for the regex */
163 str = '/' ~ str ~ '/';
165 /* add optional case insensitivity flag */
166 if(! mp.config.case_sensitive_search)
173 sub mp.search_dir(doc, str, dir)
174 /* search str and put the current position there, with direction */
179 str = mp.last_search;
181 mp.last_search = str;
186 str = mp.prefix_regex(str);
197 /* search backwards */
200 l = splice(txt.lines[y], NULL, 0, txt.x - 1);
204 while(regex(str, l, 0) == NULL && y > 0)
210 while(regex(str, txt.lines[y], x) == NULL && y < size(txt.lines))
216 /* get last coords */
222 mp.set_x(doc, r[0] + r[1]);
224 /* set always to the same line */
225 if(mp.config.move_seek_to_line != NULL &&
226 (doc.txt.vy = doc.txt.y - mp.config.move_seek_to_line) < 0)
234 sub mp.search(doc, str)
235 /* search str and put the current position there, downwards */
237 mp.search_dir(doc, str, 1);
241 sub mp.search_back(doc, str)
242 /* search str and put the current position there, backwards */
244 mp.search_dir(doc, str, -1);
248 sub mp.replace_1(doc, this, that)
249 /* searches 'this' and replaces it with 'that', once */
253 if((c = mp.search(doc, this)) != NULL)
260 txt.lines[txt.y] = sregex(mp.prefix_regex(this),
261 txt.lines[txt.y], that, c[0]);
263 /* move to correct position */
265 mp.set_x(doc, c[0] + c[1]);
274 sub mp.replace(doc, this, that)
275 /* replaces 'this' with 'that', may be globally */
277 while(mp.replace_1(doc, this, that))
279 if(!mp.config.global_replace) break;
284 sub mp.seek_prev_or_next_char(doc, func)
285 /* moves to next or previous occurent of current char */
289 /* get current char */
290 local w = splice(txt.lines[txt.y], NULL, txt.x, 1);
292 /* move one char right */
295 /* search for it (mp.search() or mp.search_back()) */
296 func(doc, '\' ~ w[1]);
303 sub mp.grep(rx, spec)
304 /* Greps str in the files in spec. Returns NULL if no file matched the glob()
305 (or glob() is unsupported), an empty list if the string was not found or
306 an array with the matches, that are three-element arrays with the file name,
307 the line number and the line that matched */
311 /* if spec is empty, set as NULL (meaning "glob everything") */
312 if(spec eq '') spec = NULL;
316 /* spec globs to NULL or empty; abort */
317 if(size(all) == 0) { return(NULL); }
321 foreach(local fn, all)
325 if((f = open(fn, "r")) != NULL)
329 /* file open; now grep */
336 /* found; store line, filename and linenum */
337 push(r, [ fn, n, l ]);