Do not use backslash-b to select backspaces.
[mp-5.x.git] / mp_syntax.mpsl
blob0444036a6fc0f92ed36d7285100a40a7b8445e79
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         'detect'        =>      sub (d) {
306                 /* take the first line */
307                 local f = d.txt.lines[0];
309                 return regex(f, '|^#!\s*/usr/bin/(env )?mpsl|');
310         },
311         'section'       =>      [ '/(^[A-Za-z_]|^\/\*\*.*\*\*\/$)/' ]
314 mp.syntax.sh = {
315         'id'            =>      'sh',
316         'name'          =>      'Shell script',
317         'filenames'     =>      [ '/\.sh$/i', '/makefile/i' ],
318         'defs'          =>      [
319                 'word1',        [
320                                 mp.syn_token_list( [
321                                 "if", "then", "else", "elif",
322                                 "fi", "case", "do", "done", "esac",
323                                 "for", "until", "while", "break",
324                                 "in", "source", "alias", "cd",
325                                 "continue", "echo", "eval", "exec",
326                                 "exit", "export", "kill", "logout",
327                                 "printf", "pwd", "read", "return",
328                                 "shift", "test", "trap", "ulimit",
329                                 "umask", "unset", "wait", "cp", "rm" ]),
330                                 '/[\{\}]/'
332                 ],
333                 'word2',        [
334                                 '/\b(local|let|set)\b/',
335                                 '/[-=<>:\?\+\*\!\%&\|]+/',
336                                 '/\$\w+/',
337                                 '/\$\{\w+\}/',
338                                 "/\b[0-9[:upper:]_]+\b/"        /* all-caps words */
339                 ],
340                 'quotes',       [
341                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",     /* double-quoted strings */
342                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/",         /* single-quoted strings */
343                         "/\b-?[0-9]+\b/",                       /* numbers */
344                         "/\b0x[0-9a-f]+\b/i",                   /* hex numbers */
345                         "/\([A-Za-z0-9_]+\)/",                  /* parens */
346                         [ "/`/", "/`/" ],                       /* backticks */
347                         [ "/<<[\"']?EOF[\"']?;$/m", "/^EOF$/m" ]        /* 'document here' */
348                 ],
349                 'comments',     [ "/#.*$/m" ]
350         ],
351         'detect'        =>      sub (d) {
352                 /* take the first line */
353                 local f = d.txt.lines[0];
355                 /* is it a 'she-bang' for usual shells? */
356                 return regex(f, '/^#!\s*/bin/(sh|bash|csh|dash|ksh)/') ||
357                        regex(f, '/^#!\s*/usr/bin/make -f/');
358         },
359         'section'       =>      [ '/(^\w+\(\))/', '/^[A-Za-z0-9_\.-]+:/' ]
363 mp.syntax.html = {
364         'id'            =>      'html',
365         'name'          =>      'HTML',
366         'filenames'     =>      [ '/\.html$/i', '/\.htm$/i' ],
367         'defs'          =>      [
368                 'word1',        [
369                                 "/<[\/]?[ \t]*(" ~
370                                 join([
371                                 "a", "abbr", "acronym", "address",
372                                 "area", "b", "base", "bdo", "big",
373                                 "blockquote", "body", "br", "button",
374                                 "caption", "center", "cite", "code", "col",
375                                 "colgroup", "dd", "del", "dfn", "div",
376                                 "dl", "dt", "em", "fieldset", "form",
377                                 "h1", "h2", "h3", "h4", "h5", "h6",
378                                 "head", "hr", "html", "i", "img",
379                                 "input", "ins", "kbd", "label", "legend",
380                                 "li", "link", "map", "meta", "noscript",
381                                 "object", "ol", "optgroup", "option",
382                                 "p", "param", "pre", "q", "samp",
383                                 "script", "select", "small", "span",
384                                 "strong", "style", "sub", "sup", "table",
385                                 "tbody", "td", "textarea", "tfoot", "th",
386                                 "thead", "title", "tr", "tt", "ul",
387                                 "var" ], "|") ~
388                                 ")[^<>]*>/i"
389                 ],
390                 'word2',        [
391                                 mp.syn_token_list( [
392                                  "!DOCTYPE", "class", "type",
393                                 "cellspacing", "cellpadding",
394                                 "href", "align", "valign", "name", "lang",
395                                 "value", "action", "width", "height",
396                                 "content", "http-equiv", "src", "alt",
397                                 "bgcolor", "text", "link", "vlink", "alink",
398                                 "media" ])
399                 ],
400                 'quotes',       [
401                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",     /* double-quoted strings */
402                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/"          /* single-quoted strings */
403                 ],
404                 'comments',     [
405                                 [ '/<!--/', '/-->/' ]
406                 ]
407         ],
408         'section'       =>      [ '/<\s*h[0-9]\s*>/' ]
412 mp.syntax.conf = {
413         'id'            =>      'conf',
414         'name'          =>      'Config file',
415         'filenames'     =>      [ '/\.conf$/i', '/\.cfg$/i', '/^.*\/?\.[0-9a-z_-]+rc$/i' ],
416         'defs'          =>      [
417                 'word1',        [ '/^\[.+\]$/m' ],
418                 'word2',        [ "/^[^:=\n]+[:=]/m" ],
419                 'quotes',       [
420                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",     /* double-quoted strings */
421                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/",         /* single-quoted strings */
422                         "/\b-?[0-9]+\b/",                       /* numbers */
423                         "/\b0x[0-9a-f]+\b/i"                    /* hex numbers */
424                 ],
425                 'comments',     [ "/#.*$/m" ]
426         ]
430 mp.syntax.php = {
431         'id'            =>      'php',
432         'name'          =>      'PHP',
433         'filenames'     =>      [ '/\.php[345]?$/i', '/\.inc$/i' ],
434         'defs'          =>      [
435                 'word1',        [
436                                 mp.syn_token_list( [
437                                 "and", "array", "as",
438                                 "bool", "boolean", "break", "case", "class",
439                                 "const", "continue", "declare", "default",
440                                 "die", "do", "double", "echo", "else", "elseif",
441                                 "empty", "enddeclare", "endfor", "endforeach",
442                                 "endif", "endswitch", "endwhile", "eval",
443                                 "exit", "extends", "__FILE__", "float", "for",
444                                 "foreach", "function", "cfunction", "global",
445                                 "if", "include", "include_once", "int",
446                                 "integer", "isset", "__LINE__", "list", "new",
447                                 "object", "old_function", "or", "print", "real",
448                                 "require", "require_once", "return",
449                                 "static", "string", "switch", "unset", "use",
450                                 "var", "while", "xor", 'true', 'false' ]
451                                 )
452                 ],
453                 'word2',        [ '/\$\w+/', '/[-=<>:\?\+\*\!\%&\|]+/' ],
454                 'quotes',       [
455                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",     /* double-quoted strings */
456                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/",         /* single-quoted strings */
457                         "/\b-?[0-9]+\b/",                       /* numbers */
458                         "/\b0x[0-9a-f]+\b/i",                   /* hex numbers */
459                         "/\b[0-9[:upper:]_]+\b/",               /* all-caps words */
460                         [ "/`/", "/`/" ]                        /* backticks */
461                 ],
462                 'comments',     [
463                         [ '|/\*|', '|\*/|' ],                   /* C-like */
464                         '|//.*$|m'                              /* C++ */
465                 ]
466         ]
469 mp.syntax.python = {
470         'id'            =>      'python',
471         'name'          =>      'Python',
472         'filenames'     =>      [ '/\.py$/i' ],
473         'defs'          =>      [
474                 'word1',        [
475                         mp.syn_token_list( [
476                                 "and", "assert", "break", "class", "continue",
477                                 "def", "del", "elif", "else", "except", "exec",
478                                 "finally", "for", "from", "if", "import", "in",
479                                 "is", "lambda", "not", "or", "pass", "print",
480                                 "raise", "return", "try", "while", "yield"
481                                 ]
482                         )
483                 ],
484                 'word2',        [ '/global/', '/\$\w+/', '/[-=<>:\?\+\*\!\%&\|{}]+/' ],
485                 'quotes',       [
486                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",     /* double-quoted strings */
487                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/",         /* single-quoted strings */
488                         "/\b-?[0-9]+\b/",                       /* numbers */
489                         "/\b0x[0-9a-f]+\b/i",                   /* hex numbers */
490                         "/\b[0-9[:upper:]_]+\b/",               /* all-caps words */
491                         [ "/`/", "/`/" ]                        /* backticks */
492                 ],
493                 'comments',             [ "/#.*$/m" ],
494                 'documentation',        [ [ '/""".+[^"]$/m', '/"""$/m' ] ]
495         ],
496         'detect'        =>      sub (d) {
497                 /* take the first line */
498                 local f = d.txt.lines[0];
500                 /* is it a 'she-bang'? */
501                 return regex(f, '/^#!\s*/usr/bin/(env )?python/');
502         },
503         'section'       =>      [ '/^[ \t]*def/' ]
506 mp.syntax.ruby = {
507         'id'            =>      'ruby',
508         'name'          =>      'Ruby',
509         'filenames'     =>      [ '/\.rb$/i' ],
510         'defs'          =>      [
511                 'word1',        [
512                         mp.syn_token_list( [
513                                 "BEGIN", "END", "alias", "and", "begin",
514                                 "break", "case", "class", "def", "defined",
515                                 "do", "else", "elsif", "end", "ensure",
516                                 "false", "for", "if", "in", "module", "next",
517                                 "nil", "not", "or", "redo", "rescue", "retry",
518                                 "return", "self", "super", "then", "true",
519                                 "undef", "unless", "until", "when", "while",
520                                 "yield", "require", "include" ]
521                         )
522                 ],
523                 'word2',        [ '/[-=<>:\?\+\*\!\%&\|{}]+/', '/=(begin|end)/' ],
524                 'quotes',       [
525                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",     /* double-quoted strings */
526                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/",         /* single-quoted strings */
527                         "/\b-?[0-9]+\b/",                       /* numbers */
528                         "/\b0x[0-9a-f]+\b/i",                   /* hex numbers */
529                         "/\b[0-9[:upper:]_]+\b/",               /* all-caps words */
530                         [ "/`/", "/`/" ]                        /* backticks */
531                 ],
532                 'comments',     [ "/#.*$/m" ]
533         ],
534         'detect'        =>      sub (d) {
535                 /* take the first line */
536                 local f = d.txt.lines[0];
538                 /* is it a 'she-bang'? */
539                 return regex(f, '/^#!\s*/usr/bin/(env )?ruby/');
540         }
543 mp.syntax.diff = {
544     id:         'diff',
545     name:       'diff',
546     filenames:  [
547         '/\.diff$/i',
548         '/\.patch$/i'
549     ],
550     defs: [
551         'word1',    [ '/^\+.+$/m' ],
552         'word2',    [ '/^\-.+$/m' ],
553         'quotes',   [ '/^@@.+@@.*$/m' ]
554     ],
555     section:        [ '/^--- ', '/^@@/' ],
556     detect:         sub (d) {
557         local r, n;
559         n = 0;
560         while (n < 100) {
561             if (regex(d.txt.lines[n++], '/^--- /'))
562                 break;
563         }
565         if (regex(d.txt.lines[n++], '/^\+\+\+ /') &&
566             regex(d.txt.lines[n++], '/^@@ /'))
567             r = 1;
569         return r;
570     }
573 mp.syntax.commit_msg = {
574         'id'            =>      'commit_msg',
575         'name'          =>      'VCS commit message',
576         'filenames'     =>      [ '/sv[kn]-commit.*\./' ],
577         'defs'          =>      [
578                 'word1',        [ '/^AM? .+$/m' ],
579                 'word2',        [ '/^D .+$/m' ],
580                 'quotes',       [ '/^M .+$/m' ],
581                 'comments',     [ '/^=== .+$/m', '/^--.+$/m' ]
582         ]
585 mp.syntax.po = {
586         'id'            =>      'po',
587         'name'          =>      'Gettext file',
588         'filenames'     =>      [ '/\.po$/i' ],
589         'defs'          =>      [
590                 'word1',        [ '/^msgid/m' ],
591                 'word2',        [ '/^msgstr/m' ],
592                 'comments',     [ "/#.*$/m" ],
593                 'quotes',       [
594                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/"      /* double-quoted strings */
595                 ]
596         ]
599 mp.syntax.xml = {
600         'id'            =>      'xml',
601         'name'          =>      'XML / XGML',
602         'filenames'     =>      [ '/\.xml$/i', '/\.sgml$/i' ],
603         'defs'          =>      [
604                 'word1',        [ '/<[^>]+>/' ],
605                 'word2',        [ '/<\?[^\?]+\?>/' ],
606                 'quotes',       [
607                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",     /* double-quoted strings */
608                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/"          /* single-quoted strings */
609                 ],
610                 'comments',     [
611                                 [ '/<!--/', '/-->/' ]
612                 ]
613         ],
614         'detect'        =>      sub (d) {
615                 /* take the first line */
616                 local f = d.txt.lines[0];
618                 return regex(f, '/<\?xml/');
619         }
622 mp.syntax.make_output = {
623         'id'            =>      'make_output',
624         'name'          =>      'Make output',
625         'defs'          =>      [
626                 'word1',        [ "/^.*warning:.*$/m" ],
627                 'word2',        [ "/^.*error:.*$/m" ]
628         ]
631 mp.syntax.euphoria = {
632         'id'            =>      'euphoria',
633         'name'          =>      'euphoria',
634         'filenames'     =>      [ '/\.e$/', '/\.eu$/', '/\.ew$/', '/\.ed$/',
635                                 '/\.ex$/', '/\.exw$/', '/\.exu$/' ],
636         'defs'          =>      [
637                 'word1',        [
638                         mp.syn_token_list( [
639                                 "as", "and", "break", "by", "case", "constant", "continue", "do", "end",
640                                 "else", "elsif", "elsifdef",
641                                 "exit", "entry", "enum", "export", "for", "function", "global", "include",
642                                 "if", "ifdef", "label",
643                                 "not", "or", "procedure", "return", "retry", "switch", "then", "type",
644                                 "to", "while", "with", "without", "xor"
645                         ] )
646                 ],
647                 'word2',        [
648                         mp.syn_token_list( [
649                                 "atom", "integer", "sequence", "object"
650                         ] )
651                 ],
652                 'quotes',       [
653                         "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/",    /* double-quoted strings */
654                         "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/",        /* single-quoted strings */
655                         "/\b-?#[0-9A-F]+\b/",                /* hex numbers */
656                         "/\b-?[0-9]+\b/",                /* numbers */
657                         "/\b[0-9[:upper:]_]+\b/"            /* all-caps words */
658                 ],
659                 'comments',             [ "/--.*$/m" ]
660         ],
661         'section'       =>      [ '/^[ \t]*(global|export)*[ \t]*(function|procedure)/' ]
665 mp.syntax.mp_templates = {
666         'id'            => 'mp_templates',
667         'name'          => 'Minimum Profit template file',
668         'filenames'     => [ '/\.mp_templates$/' ],
669         'defs'          => [
670                 'documentation',        [ '/^%%.*$/m' ]
671         ],
672         'section'       => [ '/^%%/' ]
676 mp.syntax.hex_view = {
677         'id'            => 'hex_view',
678         'name'          => 'Hexadecimal view',
679         'defs'          => [
680                 'word1',                [ "/^\| [0-9A-F]+ \|/m" ],
681                 'word2',                [ "/\|/" ]
682         ]
686 mp.syntax.grutatxt = {
687         'id'            => 'grutatxt',
688         'name'          => 'Grutatxt',
689         'defs'          => [
690                 'word1',        [ "/[^\n]+\n[-=~]+\n/", "/ \* [^.\n:]+:/" ],
691                 'comments',     [ "/\b_[^ \t\n][^_\n]*[^ \t\n]_\b/" ],
692                 'word2',        [ "/\*[^ \t\n][^\*\n]*[^ \t\n]\*/" ],
693                 'quotes',       [ "/`[^ \t\n][^'\n]*[^ \t\n]'/" ]
694         ],
695         'section'       => [ "/^[-=~]+$/" ],
696         'detect'        =>      sub (d) {
697                 local n = 0;
699                 /* search the first lines for a heading */
700                 while (n < 40) {
701                         local l = d.txt.lines[n++];
703                         if (l == NULL)
704                                 break;
706                         if (regex(l, "/^[-=~]+$/"))
707                                 return 1;
708                 }
710                 return 0;
711         }
715 /** code **/
717 sub mp.detect_syntax(doc)
718 /* tries to detect the syntax of a document */
720         doc.syntax = NULL;
722         /* loops the syntax highlight definitions */
723         foreach (n, keys(mp.syntax)) {
724                 local s = mp.syntax[n];
726                 /* test the extensions */
727                 foreach (ext, s.filenames) {
728                         if (regex(doc.name, ext)) {
729                                 doc.syntax = s;
730                                 return;
731                         }
732                 }
733         }
735         /* not by extension? try the 'detect' subroutine */
736         foreach (n, keys(mp.syntax)) {
737                 local s = mp.syntax[n];
739                 if (is_exec(s.detect) && s.detect(doc)) {
740                         doc.syntax = s;
741                         return;
742                 }
743         }
747 sub mp.help(doc, word)
749     local h;
751     foreach (c, doc.syntax.help) {
752         local f;
754         /* format the command */
755         c = sprintf(c, word);
757         /* pipe from it */
758         if ((f = popen(c, "r")) != NULL) {
759             local l;
760             h = [];
762             while ((l = read(f)) != NULL) {
763                 l = sregex(l, "/\x{0008}./g");
764                 push(h, mp.chomp(l));
765             }
767             /* fails? */
768             if (pclose(f) != 0)
769                 h = NULL;
770         }
772         /* is there already help? don't look for more */
773         if (h != NULL)
774             break;
775     }
777     if (h == NULL && doc.syntax.help_func)
778         h = doc.syntax.help_func(word);
780     return h;
785  * mp.section_list - Returns the list of sections of a document.
786  * @doc: the document
788  * Applies the `section' array of regular expressions of the
789  * document's syntax definition and returns it as an array of
790  * line and line number pairs.
792  * If the document has no syntax highlight definition, or it has
793  * one without a `section' definition, NULL is returned. Otherwise,
794  * an array of line, line number pairs is returned (it can be
795  * an empty list if the document has no recognizable sections).
796  */
797 sub mp.section_list(doc)
799     local r;
801     if (doc.syntax.section) {
802         local n, l;
804         r = [];
806         while ((l = doc.txt.lines[n]) != NULL) {
807             foreach (ex, doc.syntax.section) {
808                 if (regex(l, ex)) {
809                     push(r, [ l, n ]);
810                     break;
811                 }
812             }
814             n++;
815         }
816     }
818     return r;
823  * mp.c_gather_help - Gathers help in C-style files
824  * @word: the help term
826  * Searches in all applicable files for code snippets that may
827  * conform help for @word (mp_doccer style help, struct or
828  * function definitions, etc).
830  * Returns an array of text with the gathered information.
831  */
832 sub mp.c_gather_help(word)
834     local h = [];
836     foreach (fn, glob()) {
837         local f;
838         local ok = 0;
840         /* test if this file is relevant to C */
841                 foreach (ext, mp.syntax.c.filenames) {
842                         if (regex(fn, ext)) {
843                 ok = 1;
844                 break;
845                         }
846         }
848         if (ok && (f = open(fn, "r")) != NULL) {
849             local l;
850             local n = 0;
852             while ((l = read(f)) != NULL) {
853                 n++;
854                 local where = fn ~ ':' ~ n ~ ':';
856                 l = mp.chomp(l);
858                 if (regex(l, "/^[ \t]*#define[ \t]+" ~ word ~ "/")) {
859                     /* it's a CPP define */
860                     push(h, where, l);
861                 }
862                 else
863                 if (regex(l, "/^[A-Za-z_]+.*[ \t]+" ~ word ~ "[ \t]*\(.*/")) {
864                     /* it's a function definition or prototype */
865                     push(h, where, l);
866                 }
867                 else
868                 if (regex(l, "/^(typedef[ \]*|static[ \]*)?struct[ \t]+.*" ~ word ~ ".*\{/")) {
869                     /* it's a structure definition */
870                     push(h, where, l);
872                     /* add up to the end of the struct */
873                     while ((l = read(f)) != NULL) {
874                         n++;
875                         push(h, l);
877                         if (regex(l, "/^\};/"))
878                             break;
879                     }
880                 }
881                 else
882                 if (regex(l, "/^\/\*\*$/")) {
883                     /* mp_doccer help: is it for this word? */
884                     l = read(f);
885                     n++;
887                     if (regex(l, "/ \* " ~ word ~ " - /")) {
888                         /* it is; dump up to a ; */
889                         push(h, where, '/' ~ '**', l);
891                         while ((l = read(f)) != NULL) {
892                             n++;
893                             if (regex(l, "/^\{/"))
894                                 break;
896                             push(h, l);
897                         }
898                     }
899                 }
900             }
902             close(f);
903         }
904     }
906     if (size(h)) {
907         ins(h, sprintf(L("Gathered information on %s:"), word), 0);
908         ins(h, '', 1);
909     }
910     else
911         h = NULL;
913     return h;