Do not mark the help topic as the last search.
[mp-5.x.git] / mp_syntax.mpsl
blobabe5ff65d6e917ee03e6a4d0a8658dbbbfe4a6cd
1 /*
3     Minimum Profit 5.x
4     A Programmer's Text Editor
6     Syntax highlight definitions.
8     Copyright (C) 1991-2011 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 /** editor actions **/
30 sub mp.actions.help(d) {
31     local w, h, r;
33     /* get the special word regex separator for help (if any) */
34     r = d.syntax.help_word_regex;
36     /* if there is a word at the cursor position,
37         try searching for help */
38     if ((w = mp.get_word(d, r)) != NULL) {
39         mp.busy(1);
40         h = mp.help(d, w);
41         mp.busy(0);
42     }
44     if (h == NULL)
45         mp.alert(sprintf(L("No help for '%s'."), w));
46     else {
47         local hd;
49         hd = mp.open(sprintf(L("<help on '%s'>"), w));
51         hd.txt.lines    = h;
52         hd.txt.y        = 0;
53         hd.read_only    = 1;
55         hd.syntax = d.syntax;
56     }
59 sub mp.actions.section_list(d) {
60     local l = mp.section_list(d);
62     if (l == NULL)
63         mp.alert(L("No detection for sections for this mode."));
64     else {
65         if (size(l) == 0)
66             mp.alert(L("No sections were found in this document."));
67         else {
68             local pos = -1;
70             /* set pos to the section nearest the cursor */
71             foreach (e, l) {
72                 if (e[1] > d.txt.y)
73                     break;
75                 pos++;
76             }
78             if (pos < 0 || pos > size(l))
79                 pos = 0;
81             local r = mp.form(
82                 [
83                     {
84                         label:  L("Section list"),
85                         type:   'list',
86                         list:   map(l, sub (e) { e[0]; }),
87                         value:  pos
88                     }
89                 ]
90             );
92             if (r != NULL) {
93                 mp.search_set_y(d, l[r[0]][1]);
94                 mp.set_x(d, 0);
95             }
96         }
97     }
100 /** default key bindings **/
102 mp.keycodes['f1']       = "help";
103 mp.keycodes['ctrl-d']   = "section_list";
105 /** action descriptions **/
107 mp.actdesc['help']          = LL("Help on word over cursor");
108 mp.actdesc['section_list']  = LL("Section list...");
110 /** data **/
112 mp.syntax = {};
114 /** syntax definitions **/
116 sub mp.syn_token_list(l) { '/\b(' ~ join(l, "|") ~ ')\b/'; }
118 mp.syntax.c = {
119         'id'            =>      'c',
120         'name'          =>      'C / C++',
121         'filenames'     =>      [ '/\.c$/i', '/\.h$/i', '/\.l$/i', '/\.y$/i', '/\.d$/i',
122                                 '/\.cpp$/i', '/\.hpp$/i', '/\.c++$/i', '/\.cxx$/i', '/\.xpm$/i' ],
123         'help'          =>      [ "man 2 %s", "man 3 %s" ],
124     'help_func' =>  sub (w) { mp.c_gather_help(w); },
125         'defs'          =>      [
126                 'word1',        [
127                                 mp.syn_token_list( [
128                                 "auto", "break", "case", "catch", "class", "const", "const_cast",
129                                 "continue", "default", "delete", "do", "dynamic_cast", "else", "enum",
130                                 "explicit", "extern", "for", "friend", "goto", "if", "inline", "mutable",
131                                 "namespace", "new", "operator", "private", "protected", "public",
132                                 "register", "reinterpret_cast", "restrict", "return", "sizeof", "static",
133                                 "static_cast", "struct", "switch", "template", "this", "throw", "try",
134                                 "typedef", "typeid", "typename", "union", "using", "virtual", "volatile",
135                                 "while", "not", "not_eq", "xor", "xor_eq", "or",  "or_eq", "and", "and_eq",
136                                 "bitor", "bitand", "compl"
137                                 ])
138                 ],
139                 'word2',        [
140                                 mp.syn_token_list( [
141                                 "bool", "char", "double", "float", "int", "long", "short", "signed",
142                                 "unsigned", "wchar_t", "size_t", "ssize_t", "off_t", "wchar_t",
143                                 "ptrdiff_t", "void", "sig_atomic_t", "fpos_t", "clock_t", "time_t",
144                                 "va_list", "jmp_buf", "FILE", "DIR", "div_t", "ldiv_t", "mbstate_t",
145                                 "wctrans_t", "wint_t", "wctype_t", "complex", "int8_t", "int16_t",
146                                 "int32_t", "int64_t", "uint8_t", "uint16_t", "uint32_t", "uint64_t",
147                                 "int_least8_t", "int_least16_t", "int_least32_t", "int_least64_t",
148                                 "uint_least8_t", "uint_least16_t", "uint_least32_t", "uint_least64_t",
149                                 "int_fast8_t", "int_fast16_t", "int_fast32_t", "int_fast64_t",
150                                 "uint_fast8_t", "uint_fast16_t", "uint_fast32_t", "uint_fast64_t",
151                                 "intptr_t", "uintptr_t", "intmax_t", "uintmax_t"
152                                  ]),
153                                 '/[-=<>:\?\+\*\/\!\%&\|~\^\.]+/'
154                 ],
155                 'quotes',       [
156                         /* from http://ad.hominem.org/log/2005/05/quoted_strings.php */
157                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",     /* double-quoted strings */
158                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/",         /* single-quoted strings */
159                         "/\b-?[0-9]+\b/",                       /* numbers */
160                         "/\b[0-9]+e[0-9]+\b/",                  /* numbers in exp format */
161                         "/\b0[0-7]+\b/",                        /* octal numbers */
162                         "/\b0x[0-9a-f]+\b/i",                   /* hex numbers */
163                         "/\b__(DATE|FILE|LINE|STDC|TIME|STDC_HOSTED|STDC_VERSION|func)__\b/", /* ISO macros */
164                         "/\b(true|false|NULL)\b/"           /* symbolic constant names */
165                 ],
166                 'comments',     [
167                         [ '|/\*|', '|\*/|' ],                   /* C-like */
168                         [ '|#if 0|', '#endif' ],                /* CPP "comments" */
169                         '|//.*$|m',                             /* C++ */
170                         '/^\s*#\s*[a-z]+/m'                     /* CPP directives */
171                 ],
172                 'documentation',        [
173                         [ "|/\*\*\n|", '|\*/|' ],               /* mp_doccer */
174                         '/^/\*\*.*\*\*/$/m'                     /* section mark */
175                 ]
176         ],
177         'section'       =>      [ '/(^\/\*\*.*\*\*\/$|^#pragma mark|^[A-Za-z_])/' ]
180 mp.syntax.rc = {
181         'id'            =>      'rc',
182         'name'          =>      'Resource file',
183         'filenames'     =>      [ '/\.rc$/i' ],
184         'defs'          =>      [
185                 'word1',        [ ],
186                 'word2',        [ ],
187                 'quotes',       [
188                         /* from http://ad.hominem.org/log/2005/05/quoted_strings.php */
189                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",     /* double-quoted strings */
190                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/",         /* single-quoted strings */
191                         "/\b-?[0-9]+\b/",                       /* numbers */
192                         "/\b0x[0-9a-f]+\b/i",                   /* hex numbers */
193                         "/\b[0-9[:upper:]_]+\b/"                /* all-caps words */
194                 ],
195                 'comments',     [
196                         [ '|/\*|', '|\*/|' ],                   /* C-like */
197                         '|//.*$|m',                             /* C++ */
198                         '/^\s*#[a-z]+/m'                        /* CPP directives */
199                 ]
200         ]
203 mp.syntax.perl = {
204         'id'            =>      'perl',
205         'name'          =>      'Perl',
206         'filenames'     =>      [ '/\.pl$/i', '/\.pm$/i' ],
207         'help'          =>      [ 'perldoc -f %s', 'perldoc %s' ],
208         'help_word_regex' =>    '/[A-Z_][A-Z0-9_:]*/i',
209         'defs'  =>      [
210                 'word1',        [
211                                 mp.syn_token_list( [
212                                 "for", "if", "next", "last", "else", "elsif",
213                                 "unless", "while", "shift", "unshift", "push",
214                                 "pop", "delete", "new", "bless", "return",
215                                 "foreach", "keys", "values", "sort", "grep",
216                                 "tr", "length", "system", "exec", "fork", "map",
217                                 "print", "write", "open", "close", "chop",
218                                 "chomp", "exit", "sleep", "split", "join",
219                                 "sub", "printf", "sprintf", "s", "glob",
220                                 "scalar", "my", "local", "undef", "defined",
221                                 "use", "package", "require", "ref", "can", "isa",
222                                 "qw", "qq", "eq", "ne", "or", "exists",
223                                 "and", "not", "import", "our", "caller" ]),
224                                 '/->/'
225                 ],
226                 'word2',        [
227                                 '/[:\?\+\*\/\!\$@\%&\|~\.]+/',
228                                 '/[\$@%]\w+/'
229                 ],
230                 'quotes',       [
231                         /* from http://ad.hominem.org/log/2005/05/quoted_strings.php */
232                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",     /* double-quoted strings */
233                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/",         /* single-quoted strings */
234                         "/\b-?[0-9]+\b/",                       /* numbers */
235                         "/\b0x[0-9a-f]+\b/i",                   /* hex numbers */
236                         "/\b[0-9[:upper:]_]+\b/",               /* all-caps words */
237                         [ "/q\(/", "/\)/" ],                    /* quote */
238                         [ "/qw\(/", "/\)/" ],                   /* quote word */
239                         '/\w+\s*=>/',                           /* barewords as hash keys 1 */
240                         '/\{\s*-?\w+\s*\}/',                    /* barewords as hash keys 2 */
241                         [ "/`/", "/`/" ],                       /* backticks */
242                         [ "/<<[\"']?EOF.*$/m", "/^EOF$/m" ]     /* 'document here' */
243                 ],
244                 /* color all => as word2 */
245                 'word2',        [ '/=>/' ],
246                 /* color curly brackets as word */
247                 'word',         [ '/[{}]/' ],
248                 'comments',     [
249                         "/#.*$/m"                               /* Comments */
250                 ],
251                 'documentation',        [
252                         "/__END__\n.*$/",                       /* __END__ */
253                         '/^## .*$/m',                           /* section mark */
254                         [ "/^=(head[1-4]|over|item|back|pod|begin|end|for)/m",
255                                 "/^=cut$/m" ]                   /* POD */
256                 ]
257         ],
258         'detect'        =>      sub (d) {
259                 /* take the first line */
260                 local f = d.txt.lines[0];
262                 /* is it a 'she-bang' for Perl? */
263                 return regex(f, '/^#!\s*/usr/bin/(env )?perl/');
264         },
265         'section'       =>      [ '/(^sub \w+|^package|^## )/' ]
269 mp.syntax.mpsl = {
270         'id'            =>      'mpsl',
271         'name'          =>      'MPSL',
272         'filenames'     =>      [ '/\.mpsl?$/i' ],
273         'defs'          =>      [
274                 'word1',        [
275                                 mp.syn_token_list( [
276                                         'if', 'else', 'while', 'foreach',
277                                         'sub', 'break', 'return', 'eq', 'ne' ]),
278                                 mp.syn_token_list( keys(MPSL.CORE) ),
279                                 '/[\{\}]/'
280                 ],
281                 'word2',        [
282                                 "/(NULL|local)/",
283                                 '/[-=<>\?\+\*\/\!\%&\|]+/'
284                 ],
285                 'quotes',       [
286             /* from http://ad.hominem.org/log/2005/05/quoted_strings.php */
287             "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
288             "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/",     /* single-quoted strings */
289             "/\b-?[0-9]+\b/",                           /* numbers */
290             "/\b0x[0-9a-f]+\b/i",                       /* hex numbers */
291             '/\w+\s*:/',                                /* JS-type hashes */
292             "/\b[0-9[:upper:]_]+\b/"                    /* all-caps words */
293         ],
294         'word2',    [
295             '/:/'
296         ],
297                 'comments',     [
298                         [ '|/\*|', '|\*/|' ]                    /* C-like */
299                 ],
300                 'documentation',        [
301                         [ "|/\*\*\n|", '|\*/|' ],               /* mp_doccer */
302                         '/^/\*\*.*\*\*/$/m'                     /* section mark */
303                 ]
304         ],
305         'section'       =>      [ '/(^[A-Za-z_]|^\/\*\*.*\*\*\/$)/' ]
308 mp.syntax.sh = {
309         'id'            =>      'sh',
310         'name'          =>      'Shell script',
311         'filenames'     =>      [ '/\.sh$/i', '/makefile/i' ],
312         'defs'          =>      [
313                 'word1',        [
314                                 mp.syn_token_list( [
315                                 "if", "then", "else", "elif",
316                                 "fi", "case", "do", "done", "esac",
317                                 "for", "until", "while", "break",
318                                 "in", "source", "alias", "cd",
319                                 "continue", "echo", "eval", "exec",
320                                 "exit", "export", "kill", "logout",
321                                 "printf", "pwd", "read", "return",
322                                 "shift", "test", "trap", "ulimit",
323                                 "umask", "unset", "wait", "cp", "rm" ]),
324                                 '/[\{\}]/'
326                 ],
327                 'word2',        [
328                                 '/\b(local|let|set)\b/',
329                                 '/[-=<>:\?\+\*\!\%&\|]+/',
330                                 '/\$\w+/',
331                                 '/\$\{\w+\}/',
332                                 "/\b[0-9[:upper:]_]+\b/"        /* all-caps words */
333                 ],
334                 'quotes',       [
335                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",     /* double-quoted strings */
336                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/",         /* single-quoted strings */
337                         "/\b-?[0-9]+\b/",                       /* numbers */
338                         "/\b0x[0-9a-f]+\b/i",                   /* hex numbers */
339                         "/\([A-Za-z0-9_]+\)/",                  /* parens */
340                         [ "/`/", "/`/" ],                       /* backticks */
341                         [ "/<<[\"']?EOF[\"']?;$/m", "/^EOF$/m" ]        /* 'document here' */
342                 ],
343                 'comments',     [ "/#.*$/m" ]
344         ],
345         'detect'        =>      sub (d) {
346                 /* take the first line */
347                 local f = d.txt.lines[0];
349                 /* is it a 'she-bang' for usual shells? */
350                 return regex(f, '/^#!\s*/bin/(sh|bash|csh|dash|ksh)/') ||
351                        regex(f, '/^#!\s*/usr/bin/make -f/');
352         },
353         'section'       =>      [ '/(^\w+\(\))/', '/^[A-Za-z0-9_\.-]+:/' ]
357 mp.syntax.html = {
358         'id'            =>      'html',
359         'name'          =>      'HTML',
360         'filenames'     =>      [ '/\.html$/i', '/\.htm$/i' ],
361         'defs'          =>      [
362                 'word1',        [
363                                 "/<[\/]?[ \t]*(" ~
364                                 join([
365                                 "a", "abbr", "acronym", "address",
366                                 "area", "b", "base", "bdo", "big",
367                                 "blockquote", "body", "br", "button",
368                                 "caption", "center", "cite", "code", "col",
369                                 "colgroup", "dd", "del", "dfn", "div",
370                                 "dl", "dt", "em", "fieldset", "form",
371                                 "h1", "h2", "h3", "h4", "h5", "h6",
372                                 "head", "hr", "html", "i", "img",
373                                 "input", "ins", "kbd", "label", "legend",
374                                 "li", "link", "map", "meta", "noscript",
375                                 "object", "ol", "optgroup", "option",
376                                 "p", "param", "pre", "q", "samp",
377                                 "script", "select", "small", "span",
378                                 "strong", "style", "sub", "sup", "table",
379                                 "tbody", "td", "textarea", "tfoot", "th",
380                                 "thead", "title", "tr", "tt", "ul",
381                                 "var" ], "|") ~
382                                 ")[^<>]*>/i"
383                 ],
384                 'word2',        [
385                                 mp.syn_token_list( [
386                                  "!DOCTYPE", "class", "type",
387                                 "cellspacing", "cellpadding",
388                                 "href", "align", "valign", "name", "lang",
389                                 "value", "action", "width", "height",
390                                 "content", "http-equiv", "src", "alt",
391                                 "bgcolor", "text", "link", "vlink", "alink",
392                                 "media" ])
393                 ],
394                 'quotes',       [
395                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",     /* double-quoted strings */
396                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/"          /* single-quoted strings */
397                 ],
398                 'comments',     [
399                                 [ '/<!--/', '/-->/' ]
400                 ]
401         ],
402         'section'       =>      [ '/<\s*h[0-9]\s*>/' ]
406 mp.syntax.conf = {
407         'id'            =>      'conf',
408         'name'          =>      'Config file',
409         'filenames'     =>      [ '/\.conf$/i', '/\.cfg$/i', '/^.*\/?\.[0-9a-z_-]+rc$/i' ],
410         'defs'          =>      [
411                 'word1',        [ '/^\[.+\]$/m' ],
412                 'word2',        [ "/^[^:=\n]+[:=]/m" ],
413                 'quotes',       [
414                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",     /* double-quoted strings */
415                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/",         /* single-quoted strings */
416                         "/\b-?[0-9]+\b/",                       /* numbers */
417                         "/\b0x[0-9a-f]+\b/i"                    /* hex numbers */
418                 ],
419                 'comments',     [ "/#.*$/m" ]
420         ]
424 mp.syntax.php = {
425         'id'            =>      'php',
426         'name'          =>      'PHP',
427         'filenames'     =>      [ '/\.php[345]?$/i', '/\.inc$/i' ],
428         'defs'          =>      [
429                 'word1',        [
430                                 mp.syn_token_list( [
431                                 "and", "array", "as",
432                                 "bool", "boolean", "break", "case", "class",
433                                 "const", "continue", "declare", "default",
434                                 "die", "do", "double", "echo", "else", "elseif",
435                                 "empty", "enddeclare", "endfor", "endforeach",
436                                 "endif", "endswitch", "endwhile", "eval",
437                                 "exit", "extends", "__FILE__", "float", "for",
438                                 "foreach", "function", "cfunction", "global",
439                                 "if", "include", "include_once", "int",
440                                 "integer", "isset", "__LINE__", "list", "new",
441                                 "object", "old_function", "or", "print", "real",
442                                 "require", "require_once", "return",
443                                 "static", "string", "switch", "unset", "use",
444                                 "var", "while", "xor", 'true', 'false' ]
445                                 )
446                 ],
447                 'word2',        [ '/\$\w+/', '/[-=<>:\?\+\*\!\%&\|]+/' ],
448                 'quotes',       [
449                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",     /* double-quoted strings */
450                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/",         /* single-quoted strings */
451                         "/\b-?[0-9]+\b/",                       /* numbers */
452                         "/\b0x[0-9a-f]+\b/i",                   /* hex numbers */
453                         "/\b[0-9[:upper:]_]+\b/",               /* all-caps words */
454                         [ "/`/", "/`/" ]                        /* backticks */
455                 ],
456                 'comments',     [
457                         [ '|/\*|', '|\*/|' ],                   /* C-like */
458                         '|//.*$|m'                              /* C++ */
459                 ]
460         ]
463 mp.syntax.python = {
464         'id'            =>      'python',
465         'name'          =>      'Python',
466         'filenames'     =>      [ '/\.py$/i' ],
467         'defs'          =>      [
468                 'word1',        [
469                         mp.syn_token_list( [
470                                 "and", "assert", "break", "class", "continue",
471                                 "def", "del", "elif", "else", "except", "exec",
472                                 "finally", "for", "from", "if", "import", "in",
473                                 "is", "lambda", "not", "or", "pass", "print",
474                                 "raise", "return", "try", "while", "yield"
475                                 ]
476                         )
477                 ],
478                 'word2',        [ '/global/', '/\$\w+/', '/[-=<>:\?\+\*\!\%&\|{}]+/' ],
479                 'quotes',       [
480                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",     /* double-quoted strings */
481                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/",         /* single-quoted strings */
482                         "/\b-?[0-9]+\b/",                       /* numbers */
483                         "/\b0x[0-9a-f]+\b/i",                   /* hex numbers */
484                         "/\b[0-9[:upper:]_]+\b/",               /* all-caps words */
485                         [ "/`/", "/`/" ]                        /* backticks */
486                 ],
487                 'comments',             [ "/#.*$/m" ],
488                 'documentation',        [ [ '/""".+[^"]$/m', '/"""$/m' ] ]
489         ],
490         'detect'        =>      sub (d) {
491                 /* take the first line */
492                 local f = d.txt.lines[0];
494                 /* is it a 'she-bang'? */
495                 return regex(f, '/^#!\s*/usr/bin/(env )?python/');
496         },
497         'section'       =>      [ '/^[ \t]*def/' ]
500 mp.syntax.ruby = {
501         'id'            =>      'ruby',
502         'name'          =>      'Ruby',
503         'filenames'     =>      [ '/\.rb$/i' ],
504         'defs'          =>      [
505                 'word1',        [
506                         mp.syn_token_list( [
507                                 "BEGIN", "END", "alias", "and", "begin",
508                                 "break", "case", "class", "def", "defined",
509                                 "do", "else", "elsif", "end", "ensure",
510                                 "false", "for", "if", "in", "module", "next",
511                                 "nil", "not", "or", "redo", "rescue", "retry",
512                                 "return", "self", "super", "then", "true",
513                                 "undef", "unless", "until", "when", "while",
514                                 "yield", "require", "include" ]
515                         )
516                 ],
517                 'word2',        [ '/[-=<>:\?\+\*\!\%&\|{}]+/', '/=(begin|end)/' ],
518                 'quotes',       [
519                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",     /* double-quoted strings */
520                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/",         /* single-quoted strings */
521                         "/\b-?[0-9]+\b/",                       /* numbers */
522                         "/\b0x[0-9a-f]+\b/i",                   /* hex numbers */
523                         "/\b[0-9[:upper:]_]+\b/",               /* all-caps words */
524                         [ "/`/", "/`/" ]                        /* backticks */
525                 ],
526                 'comments',     [ "/#.*$/m" ]
527         ],
528         'detect'        =>      sub (d) {
529                 /* take the first line */
530                 local f = d.txt.lines[0];
532                 /* is it a 'she-bang'? */
533                 return regex(f, '/^#!\s*/usr/bin/(env )?ruby/');
534         }
537 mp.syntax.diff = {
538         'id'            =>      'diff',
539         'name'          =>      'diff',
540         'filenames'     =>      [ '/\.diff$/i', '/\.patch$/i' ],
541         'defs'          =>      [
542                 'word1',        [ '/^\+.+$/m' ],
543                 'word2',        [ '/^\-.+$/m' ],
544                 'quotes',       [ '/^@@.+@@.*$/m' ]
545         ],
546         'section'       =>      [ '/^--- ', '/^@@/' ]
549 mp.syntax.commit_msg = {
550         'id'            =>      'commit_msg',
551         'name'          =>      'VCS commit message',
552         'filenames'     =>      [ '/sv[kn]-commit.*\./' ],
553         'defs'          =>      [
554                 'word1',        [ '/^AM? .+$/m' ],
555                 'word2',        [ '/^D .+$/m' ],
556                 'quotes',       [ '/^M .+$/m' ],
557                 'comments',     [ '/^=== .+$/m', '/^--.+$/m' ]
558         ]
561 mp.syntax.po = {
562         'id'            =>      'po',
563         'name'          =>      'Gettext file',
564         'filenames'     =>      [ '/\.po$/i' ],
565         'defs'          =>      [
566                 'word1',        [ '/^msgid/m' ],
567                 'word2',        [ '/^msgstr/m' ],
568                 'comments',     [ "/#.*$/m" ],
569                 'quotes',       [
570                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/"      /* double-quoted strings */
571                 ]
572         ]
575 mp.syntax.xml = {
576         'id'            =>      'xml',
577         'name'          =>      'XML / XGML',
578         'filenames'     =>      [ '/\.xml$/i', '/\.sgml$/i' ],
579         'defs'          =>      [
580                 'word1',        [ '/<[^>]+>/' ],
581                 'word2',        [ '/<\?[^\?]+\?>/' ],
582                 'quotes',       [
583                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",     /* double-quoted strings */
584                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/"          /* single-quoted strings */
585                 ],
586                 'comments',     [
587                                 [ '/<!--/', '/-->/' ]
588                 ]
589         ],
590         'detect'        =>      sub (d) {
591                 /* take the first line */
592                 local f = d.txt.lines[0];
594                 return regex(f, '/<\?xml/');
595         }
598 mp.syntax.make_output = {
599         'id'            =>      'make_output',
600         'name'          =>      'Make output',
601         'defs'          =>      [
602                 'word1',        [ "/^.*warning:.*$/m" ],
603                 'word2',        [ "/^.*error:.*$/m" ]
604         ]
607 mp.syntax.euphoria = {
608         'id'            =>      'euphoria',
609         'name'          =>      'euphoria',
610         'filenames'     =>      [ '/\.e$/', '/\.eu$/', '/\.ew$/', '/\.ed$/',
611                                 '/\.ex$/', '/\.exw$/', '/\.exu$/' ],
612         'defs'          =>      [
613                 'word1',        [
614                         mp.syn_token_list( [
615                                 "as", "and", "break", "by", "case", "constant", "continue", "do", "end",
616                                 "else", "elsif", "elsifdef",
617                                 "exit", "entry", "enum", "export", "for", "function", "global", "include",
618                                 "if", "ifdef", "label",
619                                 "not", "or", "procedure", "return", "retry", "switch", "then", "type",
620                                 "to", "while", "with", "without", "xor"
621                         ] )
622                 ],
623                 'word2',        [
624                         mp.syn_token_list( [
625                                 "atom", "integer", "sequence", "object"
626                         ] )
627                 ],
628                 'quotes',       [
629                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",    /* double-quoted strings */
630                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/",        /* single-quoted strings */
631                         "/\b-?#[0-9A-F]+\b/",                /* hex numbers */
632                         "/\b-?[0-9]+\b/",                /* numbers */
633                         "/\b[0-9[:upper:]_]+\b/"            /* all-caps words */
634                 ],
635                 'comments',             [ "/--.*$/m" ]
636         ],
637         'section'       =>      [ '/^[ \t]*(global|export)*[ \t]*(function|procedure)/' ]
641 mp.syntax.mp_templates = {
642         'id'            => 'mp_templates',
643         'name'          => 'Minimum Profit template file',
644         'filenames'     => [ '/\.mp_templates$/' ],
645         'defs'          => [
646                 'documentation',        [ '/^%%.*$/m' ]
647         ],
648         'section'       => [ '/^%%/' ]
652 mp.syntax.hex_view = {
653         'id'            => 'hex_view',
654         'name'          => 'Hexadecimal view',
655         'defs'          => [
656                 'word1',                [ "/^\| [0-9A-F]+ \|/m" ],
657                 'word2',                [ "/\|/" ]
658         ]
662 mp.syntax.grutatxt = {
663         'id'            => 'grutatxt',
664         'name'          => 'Grutatxt',
665         'defs'          => [
666                 'word1',        [ "/[^\n]+\n[-=~]+\n/", "/ \* [^.\n:]+:/" ],
667                 'comments',     [ "/\b_[^ \t\n][^_\n]*[^ \t\n]_\b/" ],
668                 'word2',        [ "/\*[^ \t\n][^\*\n]*[^ \t\n]\*/" ],
669                 'quotes',       [ "/`[^ \t\n][^'\n]*[^ \t\n]'/" ]
670         ],
671         'section'       => [ "/^[-=~]+$/" ],
672         'detect'        =>      sub (d) {
673                 local n = 0;
675                 /* search the first lines for a heading */
676                 while (n < 40) {
677                         local l = d.txt.lines[n++];
679                         if (l == NULL)
680                                 break;
682                         if (regex(l, "/^[-=~]+$/"))
683                                 return 1;
684                 }
686                 return 0;
687         }
691 /** code **/
693 sub mp.detect_syntax(doc)
694 /* tries to detect the syntax of a document */
696         doc.syntax = NULL;
698         /* loops the syntax highlight definitions */
699         foreach (n, keys(mp.syntax)) {
700                 local s = mp.syntax[n];
702                 /* test the extensions */
703                 foreach (ext, s.filenames) {
704                         if (regex(doc.name, ext)) {
705                                 doc.syntax = s;
706                                 return;
707                         }
708                 }
709         }
711         /* not by extension? try the 'detect' subroutine */
712         foreach (n, keys(mp.syntax)) {
713                 local s = mp.syntax[n];
715                 if (is_exec(s.detect) && s.detect(doc)) {
716                         doc.syntax = s;
717                         return;
718                 }
719         }
723 sub mp.help(doc, word)
725         local h;
727         foreach (c, doc.syntax.help) {
728                 local f;
730                 /* format the command */
731                 c = sprintf(c, word);
733                 /* pipe from it */
734                 if ((f = popen(c, "r")) != NULL) {
735                         local l;
736                         h = [];
738                         while ((l = read(f)) != NULL)
739                                 push(h, mp.chomp(l));
741                         /* fails? */
742                         if (pclose(f) != 0)
743                                 h = NULL;
744                 }
746                 /* is there already help? don't look for more */
747                 if (h != NULL)
748                         break;
749         }
751     if (h == NULL && doc.syntax.help_func)
752         h = doc.syntax.help_func(word);
754         return h;
759  * mp.section_list - Returns the list of sections of a document.
760  * @doc: the document
762  * Applies the `section' array of regular expressions of the
763  * document's syntax definition and returns it as an array of
764  * line and line number pairs.
766  * If the document has no syntax highlight definition, or it has
767  * one without a `section' definition, NULL is returned. Otherwise,
768  * an array of line, line number pairs is returned (it can be
769  * an empty list if the document has no recognizable sections).
770  */
771 sub mp.section_list(doc)
773     local r;
775     if (doc.syntax.section) {
776         local n, l;
778         r = [];
780         while ((l = doc.txt.lines[n]) != NULL) {
781             foreach (ex, doc.syntax.section) {
782                 if (regex(l, ex)) {
783                     push(r, [ l, n ]);
784                     break;
785                 }
786             }
788             n++;
789         }
790     }
792     return r;
797  * mp.c_gather_help - Gathers help in C-style files
798  * @word: the help term
800  * Searches in all applicable files for code snippets that may
801  * conform help for @word (mp_doccer style help, struct or
802  * function definitions, etc).
804  * Returns an array of text with the gathered information.
805  */
806 sub mp.c_gather_help(word)
808     local h = [];
810     foreach (fn, glob()) {
811         local f;
812         local ok = 0;
814         /* test if this file is relevant to C */
815                 foreach (ext, mp.syntax.c.filenames) {
816                         if (regex(fn, ext)) {
817                 ok = 1;
818                 break;
819                         }
820         }
822         if (ok && (f = open(fn, "r")) != NULL) {
823             local l;
824             local n = 0;
826             while ((l = read(f)) != NULL) {
827                 n++;
828                 local where = fn ~ ':' ~ n ~ ':';
830                 l = mp.chomp(l);
832                 if (regex(l, "/^[ \t]*#define[ \t]+" ~ word ~ "/")) {
833                     /* it's a CPP define */
834                     push(h, where);
835                     push(h, l);
836                 }
837                 else
838                 if (regex(l, "/^[A-Za-z_]+.*[ \t]+" ~ word ~ "[ \t]*\(.*/")) {
839                     /* it's a function definition or prototype */
840                     push(h, where);
841                     push(h, l);
842                 }
843                 else
844                 if (regex(l, "/^(typedef[ \]*|static[ \]*)?struct[ \t]+.*" ~ word ~ ".*\{/")) {
845                     /* it's a structure definition */
846                     push(h, where);
847                     push(h, l);
849                     /* add up to the end of the struct */
850                     while ((l = read(f)) != NULL) {
851                         n++;
852                         push(h, l);
854                         if (regex(l, "/^\};/"))
855                             break;
856                     }
857                 }
858                 else
859                 if (regex(l, "/^\/\*\*$/")) {
860                     /* mp_doccer help: is it for this word? */
861                     l = read(f);
862                     n++;
864                     if (regex(l, "/ \* " ~ word ~ " - /")) {
865                         /* it is; dump up to a ; */
866                         push(h, where);
867                         push(h, '/' ~ '**');
868                         push(h, l);
870                         while ((l = read(f)) != NULL) {
871                             n++;
872                             if (regex(l, "/^\{/"))
873                                 break;
875                             push(h, l);
876                         }
877                     }
878                 }
879             }
881             close(f);
882         }
883     }
885     if (size(h)) {
886         ins(h, sprintf(L("Gathered information on %s:"), word), 0);
887         ins(h, '', 1);
888     }
889     else
890         h = NULL;
892     return h;