Updated TODO.
[mp-5.x.git] / mp_tags.mpsl
blobabc39bc0bdbb5178da84b4b80346ef3af8f76a9c
1 /*
3     Minimum Profit 5.x
4     A Programmer's Text Editor
6     Tags.
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 /* default ctags command */
29 mp.config.ctags_cmd = "ctags *";
31 /** editor actions **/
33 mp.actions['find_tag']  = sub(d) {
35         local tag = mp.get_word(d);
37         /* ask for it, taking the word under the cursor */
38         local r = mp.form( [
39                 { 'label'       => L("Tag to seek:"),
40                   'type'        => 'text',
41                   'history'     => 'find_tag',
42                   'value'       => tag }
43                 ]);
45         if (r != NULL)
46                 mp.open_tag(r[0]);
49 mp.actions['complete'] = sub(d) {
51         local words = {};
53         mp.busy(1);
55         foreach (l, d.txt.lines)
56                 foreach (w, mp.split_by_words(l))
57                         words[w]++;
59         mp.busy(0);
61         /* if the current word happens only one time, delete it */
62         local w = mp.get_word(d);
63         if (words[w] == 1)
64                 hdel(words, w);
66         mp.complete(d, keys(words));
69 mp.actions['complete_symbol'] = sub(d) {
71         mp.long_op(mp.load_tags, 1);
73         if (!mp.complete(d, keys(mp.tags), L("Select symbol:")))
74                 mp.alert(L("No matching symbol found."));
77 /** default key bindings **/
79 mp.keycodes['ctrl-t']   =       "find_tag";
80 mp.keycodes['ctrl-u']   =       "complete";
82 /** action descriptions **/
84 mp.actdesc['find_tag']          = LL("Search tag...");
85 mp.actdesc['complete']          = LL("Complete...");
86 mp.actdesc['complete_symbol']   = LL("Symbol completion...");
88 /** code **/
90 sub mp.load_tags(force)
91 /* load a 'tags' file */
93         local f, a;
95         if ((f = open("tags", "r")) == NULL) {
96                 /* if force is set, execute the ctags command */
97                 if (!force || (f = popen(mp.config.ctags_cmd, "r")) == NULL)
98                         return;
100                 /* read (and discard) a line from it */
101                 read(f);
102                 pclose(f);
104                 /* try again */
105                 if ((f = open("tags", "r")) == NULL)
106                         return;
107         }
109         /* deletes all currently stored tags from the word/color cache */
110         foreach (l, keys(mp.tags))
111                 hdel(mp.word_color, l);
113         /* reset current tags */
114         mp.tags = {};
116         /* get the attribute for tags */
117         a = mp.colors.tag.attr;
119         while ((l = read(f))) {
120                 local t = split(l, "\t");
121                 local l, r;
123                 /* clean the regex 'markup' for the label */
124                 l = sregex(t[2], '@^/\^@', '');
125                 l = sregex(l, '@\$/;"$@', '') ~ ' [' ~ t[1] ~ ']';
127                 /* clean the regex itself */
128                 r = sregex(t[2], '@/;"$@');
129                 r = sregex(r, '@^/@');
131                 /* escape all troublesome characters */
132                 r = sregex(r, "/([\(\)\*\?\[\{\}]|\])/g", sub (m) { '\' ~ m; });
134                 /* store the tag information */
135                 mp.tags[t[0]] = {
136                         'tag'   => t[0],
137                         'file'  => t[1],
138                         'regex' => r,
139                         'label' => l
140                         };
142                 /* store the word as specially-colored */
143                 mp.word_color[t[0]] = a;
144         }
146         close(f);
150 sub mp.open_tag(tag)
151 /* opens a tag (uses GUI) */
153         local doc, a, t;
155         /* force loading of the tags file */
156         mp.long_op(mp.load_tags, 1);
158         /* greps all tags containing the tag, and returns the
159            values from mp.tags */
160         if ((t = map(grep(keys(mp.tags), '/' ~ tag ~ '/'), mp.tags)) == NULL) {
161                 mp.alert(L("Tag not found."));
162                 return;
163         }
165         /* only one? get the first one */
166         if (size(t) == 1)
167                 a = 0;
168         else {
169                 /* build a list to ask the user */
170                 local l = map(t, sub(e) { e.label; });
172                 local r = mp.form( [
173                         { 'label'       => L("Select tag:"),
174                           'type'        => 'list',
175                           'list'        => l }
176                         ]);
178                 if (r == NULL)
179                         return;
180                 a = r[0];
181         }
183         tag = t[a];
185         /* open the file */
186         doc = mp.long_op(mp.open, tag.file);
188         /* move up and search */
189         mp.move_bof(doc);
190         return mp.long_op(mp.search, doc, tag.regex);
194 sub mp.complete(d, list, label)
195 /* completes the current word given a list (uses GUI) */
197         local l, a;
198         local word = mp.get_word(d);
200         /* takes all list elements starting with word */
201         if ((l = sort(grep(list, '/^' ~ word ~ '/'))) == NULL)
202                 return 0;
204         if (size(l) == 1)
205                 a = 0;
206         else {
207                 /* more than one; ask user */
208                 local r = mp.form( [
209                         { 'label'       => label || L("Select:"),
210                           'type'        => 'list',
211                           'list'        => l }
212                         ]);
214                 if (r == NULL)
215                         return 1;
216                 a = r[0];
217         }
219         /* accepted; substitute current word */
220         mp.store_undo(d);
222         /* split line by words to take the offset of current word */
223         local r = mp.split_line_by_words(d);
224         local offset = r[1][r[2]];
226         /* substitute current word with newly selected word */
227         local w = splice(d.txt.lines[d.txt.y], l[a], offset, size(word));
229         /* change line and x cursor */
230         d.txt.lines[d.txt.y] = w[0];
231         d.txt.x = offset + size(l[a]);
233         d.txt.mod++;
235         return 1;