The 'Pipes' game is assumed as working (Closes: #1293).
[mp-5.x.git] / mp_syntax.mpsl
blob80b557ce12f5938c000b2d9680cdeddc8576c4d2
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:  [
541         '/\.diff$/i',
542         '/\.patch$/i'
543     ],
544     defs: [
545         'word1',    [ '/^\+.+$/m' ],
546         'word2',    [ '/^\-.+$/m' ],
547         'quotes',   [ '/^@@.+@@.*$/m' ]
548     ],
549     section:        [ '/^--- ', '/^@@/' ],
550     detect:         sub (d) {
551         local r, n;
553         n = 0;
554         while (n < 100) {
555             if (regex(d.txt.lines[n++], '/^--- /'))
556                 break;
557         }
559         if (regex(d.txt.lines[n++], '/^\+\+\+ /') &&
560             regex(d.txt.lines[n++], '/^@@ /'))
561             r = 1;
563         return r;
564     }
567 mp.syntax.commit_msg = {
568         'id'            =>      'commit_msg',
569         'name'          =>      'VCS commit message',
570         'filenames'     =>      [ '/sv[kn]-commit.*\./' ],
571         'defs'          =>      [
572                 'word1',        [ '/^AM? .+$/m' ],
573                 'word2',        [ '/^D .+$/m' ],
574                 'quotes',       [ '/^M .+$/m' ],
575                 'comments',     [ '/^=== .+$/m', '/^--.+$/m' ]
576         ]
579 mp.syntax.po = {
580         'id'            =>      'po',
581         'name'          =>      'Gettext file',
582         'filenames'     =>      [ '/\.po$/i' ],
583         'defs'          =>      [
584                 'word1',        [ '/^msgid/m' ],
585                 'word2',        [ '/^msgstr/m' ],
586                 'comments',     [ "/#.*$/m" ],
587                 'quotes',       [
588                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/"      /* double-quoted strings */
589                 ]
590         ]
593 mp.syntax.xml = {
594         'id'            =>      'xml',
595         'name'          =>      'XML / XGML',
596         'filenames'     =>      [ '/\.xml$/i', '/\.sgml$/i' ],
597         'defs'          =>      [
598                 'word1',        [ '/<[^>]+>/' ],
599                 'word2',        [ '/<\?[^\?]+\?>/' ],
600                 'quotes',       [
601                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",     /* double-quoted strings */
602                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/"          /* single-quoted strings */
603                 ],
604                 'comments',     [
605                                 [ '/<!--/', '/-->/' ]
606                 ]
607         ],
608         'detect'        =>      sub (d) {
609                 /* take the first line */
610                 local f = d.txt.lines[0];
612                 return regex(f, '/<\?xml/');
613         }
616 mp.syntax.make_output = {
617         'id'            =>      'make_output',
618         'name'          =>      'Make output',
619         'defs'          =>      [
620                 'word1',        [ "/^.*warning:.*$/m" ],
621                 'word2',        [ "/^.*error:.*$/m" ]
622         ]
625 mp.syntax.euphoria = {
626         'id'            =>      'euphoria',
627         'name'          =>      'euphoria',
628         'filenames'     =>      [ '/\.e$/', '/\.eu$/', '/\.ew$/', '/\.ed$/',
629                                 '/\.ex$/', '/\.exw$/', '/\.exu$/' ],
630         'defs'          =>      [
631                 'word1',        [
632                         mp.syn_token_list( [
633                                 "as", "and", "break", "by", "case", "constant", "continue", "do", "end",
634                                 "else", "elsif", "elsifdef",
635                                 "exit", "entry", "enum", "export", "for", "function", "global", "include",
636                                 "if", "ifdef", "label",
637                                 "not", "or", "procedure", "return", "retry", "switch", "then", "type",
638                                 "to", "while", "with", "without", "xor"
639                         ] )
640                 ],
641                 'word2',        [
642                         mp.syn_token_list( [
643                                 "atom", "integer", "sequence", "object"
644                         ] )
645                 ],
646                 'quotes',       [
647                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",    /* double-quoted strings */
648                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/",        /* single-quoted strings */
649                         "/\b-?#[0-9A-F]+\b/",                /* hex numbers */
650                         "/\b-?[0-9]+\b/",                /* numbers */
651                         "/\b[0-9[:upper:]_]+\b/"            /* all-caps words */
652                 ],
653                 'comments',             [ "/--.*$/m" ]
654         ],
655         'section'       =>      [ '/^[ \t]*(global|export)*[ \t]*(function|procedure)/' ]
659 mp.syntax.mp_templates = {
660         'id'            => 'mp_templates',
661         'name'          => 'Minimum Profit template file',
662         'filenames'     => [ '/\.mp_templates$/' ],
663         'defs'          => [
664                 'documentation',        [ '/^%%.*$/m' ]
665         ],
666         'section'       => [ '/^%%/' ]
670 mp.syntax.hex_view = {
671         'id'            => 'hex_view',
672         'name'          => 'Hexadecimal view',
673         'defs'          => [
674                 'word1',                [ "/^\| [0-9A-F]+ \|/m" ],
675                 'word2',                [ "/\|/" ]
676         ]
680 mp.syntax.grutatxt = {
681         'id'            => 'grutatxt',
682         'name'          => 'Grutatxt',
683         'defs'          => [
684                 'word1',        [ "/[^\n]+\n[-=~]+\n/", "/ \* [^.\n:]+:/" ],
685                 'comments',     [ "/\b_[^ \t\n][^_\n]*[^ \t\n]_\b/" ],
686                 'word2',        [ "/\*[^ \t\n][^\*\n]*[^ \t\n]\*/" ],
687                 'quotes',       [ "/`[^ \t\n][^'\n]*[^ \t\n]'/" ]
688         ],
689         'section'       => [ "/^[-=~]+$/" ],
690         'detect'        =>      sub (d) {
691                 local n = 0;
693                 /* search the first lines for a heading */
694                 while (n < 40) {
695                         local l = d.txt.lines[n++];
697                         if (l == NULL)
698                                 break;
700                         if (regex(l, "/^[-=~]+$/"))
701                                 return 1;
702                 }
704                 return 0;
705         }
709 /** code **/
711 sub mp.detect_syntax(doc)
712 /* tries to detect the syntax of a document */
714         doc.syntax = NULL;
716         /* loops the syntax highlight definitions */
717         foreach (n, keys(mp.syntax)) {
718                 local s = mp.syntax[n];
720                 /* test the extensions */
721                 foreach (ext, s.filenames) {
722                         if (regex(doc.name, ext)) {
723                                 doc.syntax = s;
724                                 return;
725                         }
726                 }
727         }
729         /* not by extension? try the 'detect' subroutine */
730         foreach (n, keys(mp.syntax)) {
731                 local s = mp.syntax[n];
733                 if (is_exec(s.detect) && s.detect(doc)) {
734                         doc.syntax = s;
735                         return;
736                 }
737         }
741 sub mp.help(doc, word)
743         local h;
745         foreach (c, doc.syntax.help) {
746                 local f;
748                 /* format the command */
749                 c = sprintf(c, word);
751                 /* pipe from it */
752                 if ((f = popen(c, "r")) != NULL) {
753                         local l;
754                         h = [];
756                         while ((l = read(f)) != NULL)
757                                 push(h, mp.chomp(l));
759                         /* fails? */
760                         if (pclose(f) != 0)
761                                 h = NULL;
762                 }
764                 /* is there already help? don't look for more */
765                 if (h != NULL)
766                         break;
767         }
769     if (h == NULL && doc.syntax.help_func)
770         h = doc.syntax.help_func(word);
772         return h;
777  * mp.section_list - Returns the list of sections of a document.
778  * @doc: the document
780  * Applies the `section' array of regular expressions of the
781  * document's syntax definition and returns it as an array of
782  * line and line number pairs.
784  * If the document has no syntax highlight definition, or it has
785  * one without a `section' definition, NULL is returned. Otherwise,
786  * an array of line, line number pairs is returned (it can be
787  * an empty list if the document has no recognizable sections).
788  */
789 sub mp.section_list(doc)
791     local r;
793     if (doc.syntax.section) {
794         local n, l;
796         r = [];
798         while ((l = doc.txt.lines[n]) != NULL) {
799             foreach (ex, doc.syntax.section) {
800                 if (regex(l, ex)) {
801                     push(r, [ l, n ]);
802                     break;
803                 }
804             }
806             n++;
807         }
808     }
810     return r;
815  * mp.c_gather_help - Gathers help in C-style files
816  * @word: the help term
818  * Searches in all applicable files for code snippets that may
819  * conform help for @word (mp_doccer style help, struct or
820  * function definitions, etc).
822  * Returns an array of text with the gathered information.
823  */
824 sub mp.c_gather_help(word)
826     local h = [];
828     foreach (fn, glob()) {
829         local f;
830         local ok = 0;
832         /* test if this file is relevant to C */
833                 foreach (ext, mp.syntax.c.filenames) {
834                         if (regex(fn, ext)) {
835                 ok = 1;
836                 break;
837                         }
838         }
840         if (ok && (f = open(fn, "r")) != NULL) {
841             local l;
842             local n = 0;
844             while ((l = read(f)) != NULL) {
845                 n++;
846                 local where = fn ~ ':' ~ n ~ ':';
848                 l = mp.chomp(l);
850                 if (regex(l, "/^[ \t]*#define[ \t]+" ~ word ~ "/")) {
851                     /* it's a CPP define */
852                     push(h, where, l);
853                 }
854                 else
855                 if (regex(l, "/^[A-Za-z_]+.*[ \t]+" ~ word ~ "[ \t]*\(.*/")) {
856                     /* it's a function definition or prototype */
857                     push(h, where, l);
858                 }
859                 else
860                 if (regex(l, "/^(typedef[ \]*|static[ \]*)?struct[ \t]+.*" ~ word ~ ".*\{/")) {
861                     /* it's a structure definition */
862                     push(h, where, l);
864                     /* add up to the end of the struct */
865                     while ((l = read(f)) != NULL) {
866                         n++;
867                         push(h, l);
869                         if (regex(l, "/^\};/"))
870                             break;
871                     }
872                 }
873                 else
874                 if (regex(l, "/^\/\*\*$/")) {
875                     /* mp_doccer help: is it for this word? */
876                     l = read(f);
877                     n++;
879                     if (regex(l, "/ \* " ~ word ~ " - /")) {
880                         /* it is; dump up to a ; */
881                         push(h, where, '/' ~ '**', l);
883                         while ((l = read(f)) != NULL) {
884                             n++;
885                             if (regex(l, "/^\{/"))
886                                 break;
888                             push(h, l);
889                         }
890                     }
891                 }
892             }
894             close(f);
895         }
896     }
898     if (size(h)) {
899         ins(h, sprintf(L("Gathered information on %s:"), word), 0);
900         ins(h, '', 1);
901     }
902     else
903         h = NULL;
905     return h;