Set the minimum width a bit wider.
[mp-5.x.git] / mp_search.mpsl
blob06c81a7c6fe05f60cacef8182caaa47b02bbc74f
1 /*
3     Minimum Profit 5.x
4     A Programmer's Text Editor
6     Search and replace.
8     Copyright (C) 1991-2008 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;
31 /* editor actions */
33 mp.actions['seek']      = sub (d) {
34         local t = mp.form( [
35                 { 'label'       => L("Text to seek:"),
36                   'type'        => 'text',
37                   'history'     => 'search' },
38                 { 'label'       => L("Case sensitive") ~ ':',
39                   'type'        => 'checkbox',
40                   'value'       => mp.config.case_sensitive_search }
41         ] );
43         if (t != NULL) {
44                 mp.config.case_sensitive_search = t[1];
46                 mp.long_op(mp.search, d, mp.backslash_codes(t[0])) ||
47                         mp.alert(L("Text not found."));
48         }
51 mp.actions['seek_next'] = sub (d) { mp.long_op(mp.search, d, NULL) ||
52                 mp.alert(L("Text not found."));
53         };
55 mp.actions['seek_prev'] = sub (d) { mp.long_op(mp.search_back, d, NULL) ||
56                 mp.alert(L("Text not found."));
57         };
59 mp.actions['replace']   = sub (d) {
61         local r = mp.form( [
62                 { 'label'       => L("Replace text:"),
63                   'type'        => 'text',
64                   'history'     => 'search'},
65                 { 'label'       => L("Replace with:"),
66                   'type'        => 'text',
67                   'history'     => 'replace'},
68                 { 'label'       => L("Case sensitive") ~ ':',
69                   'type'        => 'checkbox',
70                   'value'       => mp.config.case_sensitive_search },
71                 { 'label'       => L("Global replace:"),
72                   'type'        => 'checkbox',
73                   'value'       => mp.config.global_replace }
74         ] );
76         if (r != NULL) {
77                 mp.config.case_sensitive_search = r[2];
78                 mp.config.global_replace = r[3];
80                 mp.store_undo(d);
81                 mp.long_op(mp.replace, d, mp.backslash_codes(r[0]), mp.backslash_codes(r[1]));
82         }
85 mp.actions['seek_next_char'] = sub (d) { mp.seek_prev_or_next_char(d, mp.search); };
86 mp.actions['seek_prev_char'] = sub (d) { mp.seek_prev_or_next_char(d, mp.search_back); };
88 mp.actions['grep']      = sub (d) {
90         local r = mp.form( [
91                 { 'label'       => L("Text to seek:"),
92                   'type'        => 'text',
93                   'history'     => 'search'},
94                 { 'label'       => L("Files to grep (empty, all):"),
95                   'type'        => 'text',
96                   'history'     => 'grep'}
97         ] );
99         if (r != NULL && r[0] ne '') {
101                 local t = '<grep ' ~ r[0] ~ ' ' ~ r[1] ~ '>';
103                 if ((r = mp.long_op(mp.grep, '/' ~ r[0] ~ '/', r[1])) == NULL)
104                         mp.alert(L("File(s) not found."));
105                 else
106                 if (size(r) == 0)
107                         mp.alert(L("Text not found."));
108                 else {
109                         local l = mp.open(t);
111                         l.txt.lines = [];
112                         mp.move_bof(l);
114                         foreach (e, r)
115                                 mp.insert(l, sprintf("%s:%d: %s\n",
116                                                 e[0], e[1] + 1, e[2]));
118                         mp.move_bof(l);
120                         l.txt.mod = 0;
121                         l.read_only = 1;
122                 }
123         }
126 /* default key bindings */
128 mp.keycodes['f3']               = 'seek_next';
129 mp.keycodes['ctrl-f3']          = 'seek_prev';
130 mp.keycodes['ctrl-f']           = 'seek';
131 mp.keycodes['ctrl-r']           = 'replace';
132 mp.keycodes['ctrl-page-down']   = 'seek_next_char';
133 mp.keycodes['ctrl-page-up']     = 'seek_prev_char';
135 /* action descriptions */
136 mp.actdesc['seek']              = LL("Search text...");
137 mp.actdesc['seek_next']         = LL("Search next");
138 mp.actdesc['seek_prev']         = LL("Search previous");
139 mp.actdesc['replace']           = LL("Replace...");
140 mp.actdesc['seek_next_char']    = LL("Move to next instance of current char");
141 mp.actdesc['seek_prev_char']    = LL("Move to previous instance of current char");
142 mp.actdesc['grep']              = LL("Grep (find inside) files...");
144 /* code */
146 sub mp.prefix_regex(str)
147 /* set str to be a valid regex */
149         if (!str)
150                 return NULL;
152         /* surround with / for the regex */
153         str = '/' ~ str ~ '/';
155         /* add optional case insensitivity flag */
156         if (! mp.config.case_sensitive_search)
157                 str = str ~ 'i';
159         return str;
163 sub mp.search_dir(doc, str, dir)
164 /* search str and put the current position there, with direction */
166         local txt, x, y, r;
168         if (str == NULL)
169                 str = mp.last_search;
170         else {
171                 str = mp.prefix_regex(str);
172                 mp.last_search = str;
173         }
175         if (str == NULL)
176                 return NULL;
178         txt = doc.txt;
179         x = txt.x;
180         y = txt.y;
182         if (dir == -1) {
183                 local l;
184                 str = str ~ 'l';
186                 /* search backwards */
187                 if (txt.x) {
188                         l = splice(txt.lines[y], NULL, 0, txt.x - 1);
189                         l = l[1];
190                 }
192                 while (regex(str, l, 0) == NULL && y > 0)
193                         l = txt.lines[--y];
194         }
195         else {
196                 /* search forward */
197                 while (regex(str, txt.lines[y], x) == NULL && y < size(txt.lines)) {
198                         x = 0; y++;
199                 }
200         }
202         /* get last coords */
203         r = regex();
205         if (r) {
206                 mp.set_y(doc, y);
207                 mp.set_x(doc, r[0] + r[1]);
209                 /* set always to the same line */
210                 if (mp.config.move_seek_to_line != NULL &&
211                    (doc.txt.vy = doc.txt.y - mp.config.move_seek_to_line) < 0)
212                         doc.txt.vy = 0;
213         }
215         return r;
219 sub mp.search(doc, str)
220 /* search str and put the current position there, downwards */
222         mp.search_dir(doc, str, 1);
226 sub mp.search_back(doc, str)
227 /* search str and put the current position there, backwards */
229         mp.search_dir(doc, str, -1);
233 sub mp.replace_1(doc, this, that)
234 /* searches 'this' and replaces it with 'that', once */
236         local c;
238         if ((c = mp.search(doc, this)) != NULL) {
239                 local txt, l;
241                 txt = doc.txt;
243                 /* substitute */
244                 txt.lines[txt.y] = sregex(mp.prefix_regex(this),
245                         txt.lines[txt.y], that, c[0]);
247                 /* move to correct position */
248                 c = regex();
249                 mp.set_x(doc, c[0] + c[1]);
251                 txt.mod++;
252         }
254         return c;
258 sub mp.replace(doc, this, that)
259 /* replaces 'this' with 'that', may be globally */
261         local cnt = 0;
263         while (mp.replace_1(doc, this, that)) {
264                 cnt++;
266                 if (!mp.config.global_replace)
267                         break;
268         }
270         mp.message = {
271                 'timeout' => time() + 4,
272                 'string'  => sprintf(L("%d replaces"), cnt)
273         };
277 sub mp.seek_prev_or_next_char(doc, func)
278 /* moves to next or previous occurent of current char */
280         local txt = doc.txt;
282         /* get current char */
283         local w = splice(txt.lines[txt.y], NULL, txt.x, 1);
285         /* move one char right */
286         mp.move_right(doc);
288         /* search for it (mp.search() or mp.search_back()) */
289         local t = mp.last_search;
290         func(doc, '\' ~ w[1]);
291         mp.last_search = t;
293         /* move back */
294         mp.move_left(doc);
298 sub mp.grep(rx, spec)
299 /* Greps str in the files in spec. Returns NULL if no file matched the glob()
300    (or glob() is unsupported), an empty list if the string was not found or
301    an array with the matches, that are three-element arrays with the file name,
302    the line number and the line that matched */
304         local r, all;
306         /* if spec is empty, set as NULL (meaning "glob everything") */
307         if (spec eq '')
308                 spec = NULL;
310         all = glob(spec);
312         /* spec globs to NULL or empty; abort */
313         if (size(all) == 0)
314                 return NULL;
316         r = [];
318         foreach (fn, all) {
319                 local f;
321                 if ((f = open(fn, "r")) != NULL) {
322                         local l, n;
324                         /* file open; now grep */
325                         while (l = read(f)) {
326                                 l = mp.chomp(l);
328                                 if (regex(rx, l)) {
329                                         /* found; store line, filename and linenum */
330                                         push(r, [ fn, n, l ]);
331                                 }
333                                 n++;
334                         }
336                         close(f);
337                 }
338         }
340         return r;