4 A Programmer's Text Editor
6 Syntax highlight definitions.
8 Copyright (C) 1991-2007 Angel Ortega <angel@triptico.com>
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; either version 2
13 of the License, or (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 http://www.triptico.com
30 mp.actions['help'] = sub (d) {
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)
42 mp.alert(sprintf(L("No help for '%s'."), w));
46 d = mp.new(sprintf(L("<help on '%s'>"), w), h);
51 /* default key bindings */
53 mp.keycodes['f1'] = "help";
55 /* action descriptions */
57 mp.actdesc['help'] = LL("Help on word over cursor");
65 sub mp.syn_token_list(l) { '/\b(' ~ join("|", l) ~ ')\b/'; }
70 'filenames' => [ '/\.c$/', '/\.h$/', '/\.l$/', '/\.y$/', '/\.d$/',
71 '/\.cpp$/', '/\.hpp$/', '/\.c++$/', '/\.xpm$/' ],
72 'help' => [ "man 2 %s", "man 3 %s" ],
76 "for", "while", "if", "switch", "case", "do",
77 "else", "break", "continue", "return",
78 "default", "goto", "main", "fopen", "fclose",
79 "fgets", "fgetc", "fputs", "fputc", "fprintf",
80 "putc", "printf", "sprintf", "strcpy", "strcat",
81 "strcmp", "strncmp", "strtok", "stricmp", "strchr",
82 "strrchr", "strlen", "memcmp", "memcpy", "malloc",
83 "free", "strncpy", "strncat", "snprintf", "strstr",
84 "memset", "memcpy", "va_start", "va_end", "vsprintf",
85 "vsnprintf", "atoi", "qsort", "bsearch", "getenv",
86 "fscanf", "popen", "pclose", "realloc", "fread",
87 "fwrite", "fseek", '\{', '\}', "putchar", "fflush",
88 "wcscmp", "swprintf", "wmemcpy", "swscanf", "sscanf",
89 "wcslen", "wmemset", "wcscpy", "wcsncpy", "wcscoll",
90 "mbstowcs", "wcstombs", "wprintf", "wcschr",
91 "wcsrchr", "wcsstr", "strdup", "wctomb", "mbtowc",
92 "mbrtowc", "wcrtomb", "open", "close", "read",
93 "write", "pipe", "fork", "dup", "dup2", "wait",
94 "execl", "execlp", "execle", "execv", "execvp"
99 "char", "int", "long", "struct", "union", "const",
100 "void", "unsigned", "signed", "auto", "volatile",
101 "enum", "typedef", "float", "double", "extern",
102 "register", "short", "sizeof", "static", "far",
103 "near", "defined", "va_list", "size_t", "wchar_t",
105 '/[-=<>:\?\+\*\/\!\%&\|]+/'
108 /* from http://ad.hominem.org/log/2005/05/quoted_strings.php */
109 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
110 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
111 "/\b-?[0-9]+\b/", /* numbers */
112 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
113 "/\b[0-9[:upper:]_]+\b/" /* all-caps words */
116 [ '|/\*|', '|\*/|' ], /* C-like */
117 '|//.*$|m', /* C++ */
118 '/^\s*#[a-z]+/m' /* CPP directives */
125 'name' => 'Resource file',
126 'filenames' => [ '/\.rc$/' ],
131 /* from http://ad.hominem.org/log/2005/05/quoted_strings.php */
132 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
133 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
134 "/\b-?[0-9]+\b/", /* numbers */
135 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
136 "/\b[0-9[:upper:]_]+\b/" /* all-caps words */
139 [ '|/\*|', '|\*/|' ], /* C-like */
140 '|//.*$|m', /* C++ */
141 '/^\s*#[a-z]+/m' /* CPP directives */
149 'filenames' => [ '/\.pl$/i', '/\.pm$/' ],
150 'help' => [ 'perldoc -f %s', 'perldoc %s' ],
151 'help_word_regex' => '/[A-Z_][A-Z0-9_:]*/i',
155 "for", "if", "next", "last", "else", "elsif",
156 "unless", "while", "shift", "unshift", "push",
157 "pop", "delete", "new", "bless", "return",
158 "foreach", "keys", "values", "sort", "grep",
159 "tr", "length", "system", "exec", "fork", "map",
160 "print", "write", "open", "close", "chop",
161 "chomp", "exit", "sleep", "split", "join",
162 "sub", "printf", "sprintf", "s", "glob",
163 "scalar", "my", "local", "undef", "defined",
164 "use", "package", "require", "ref", "can", "isa",
165 "qw", "qq", "eq", "ne", "or", "exists",
166 "and", "not", "import", "our", "caller" ]),
170 '/[:\?\+\*\/\!\$@\%&\|~\.]+/',
174 /* from http://ad.hominem.org/log/2005/05/quoted_strings.php */
175 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
176 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
177 "/\b-?[0-9]+\b/", /* numbers */
178 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
179 "/\b[0-9[:upper:]_]+\b/", /* all-caps words */
180 [ "/q\(/", "/\)/" ], /* quote */
181 [ "/qw\(/", "/\)/" ], /* quote word */
182 '/\w+\s*=>/', /* barewords as hash keys 1 */
183 '/\{\s*-?\w+\s*\}/', /* barewords as hash keys 2 */
184 [ "/`/", "/`/" ], /* backticks */
185 [ "/<<[\"']?EOF.*$/m", "/^EOF$/m" ] /* 'document here' */
187 /* color all => as word2 */
189 /* color curly brackets as word */
190 'word', [ '/[{}]/' ],
192 "/#.*$/m", /* Comments */
193 "/__END__\n.*$/", /* __END__ */
194 [ "/^=(head[1-4]|over|item|back|pod|begin|end|for)/m",
195 "/^=cut$/m" ] /* POD */
198 'detect' => sub (d) {
199 /* take the first line */
200 local f = d.txt.lines[0];
202 /* is it a 'she-bang' for Perl? */
203 return regex('/^#!\s*/usr/bin/(env )?perl/', f);
211 'filenames' => [ '/\.mpsl$/' ],
215 'if', 'else', 'while', 'foreach',
216 'sub', 'break', 'return', 'eq', 'ne' ]),
217 mp.syn_token_list( keys(MPSL.CORE) ),
222 '/[-=<>:\?\+\*\/\!\%&\|]+/'
225 /* from http://ad.hominem.org/log/2005/05/quoted_strings.php */
226 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
227 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
228 "/\b-?[0-9]+\b/", /* numbers */
229 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
230 "/\b[0-9[:upper:]_]+\b/" /* all-caps words */
233 [ '|/\*|', '|\*/|' ] /* C-like */
240 'name' => 'Shell script',
241 'filenames' => [ '/\.sh$/', '/makefile/i' ],
245 "if", "then", "else", "elif",
246 "fi", "case", "do", "done", "esac",
247 "for", "until", "while", "break",
248 "in", "source", "alias", "cd",
249 "continue", "echo", "eval", "exec",
250 "exit", "export", "kill", "logout",
251 "printf", "pwd", "read", "return",
252 "shift", "test", "trap", "ulimit",
253 "umask", "unset", "wait", "cp", "rm" ]),
258 '/\b(local|let|set)\b/',
259 '/[-=<>:\?\+\*\!\%&\|]+/',
262 "/\b[0-9[:upper:]_]+\b/" /* all-caps words */
265 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
266 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
267 "/\b-?[0-9]+\b/", /* numbers */
268 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
269 "/\([A-Za-z0-9_]+\)/", /* parens */
270 [ "/`/", "/`/" ], /* backticks */
271 [ "/<<[\"']?EOF[\"']?;$/m", "/^EOF$/m" ] /* 'document here' */
273 'comments', [ "/#.*$/m" ]
275 'detect' => sub (d) {
276 /* take the first line */
277 local f = d.txt.lines[0];
279 /* is it a 'she-bang' for usual shells? */
280 return regex('/^#!\s*/bin/(sh|bash|csh|dash|ksh)/', f) ||
281 regex('/^#!\s*/usr/bin/make -f/', f);
290 'filenames' => [ '/\.html$/', '/\.htm$/' ],
295 "a", "abbr", "acronym", "address",
296 "area", "b", "base", "bdo", "big",
297 "blockquote", "body", "br", "button",
298 "caption", "center", "cite", "code", "col",
299 "colgroup", "dd", "del", "dfn", "div",
300 "dl", "dt", "em", "fieldset", "form",
301 "h1", "h2", "h3", "h4", "h5", "h6",
302 "head", "hr", "html", "i", "img",
303 "input", "ins", "kbd", "label", "legend",
304 "li", "link", "map", "meta", "noscript",
305 "object", "ol", "optgroup", "option",
306 "p", "param", "pre", "q", "samp",
307 "script", "select", "small", "span",
308 "strong", "style", "sub", "sup", "table",
309 "tbody", "td", "textarea", "tfoot", "th",
310 "thead", "title", "tr", "tt", "ul",
316 "!DOCTYPE", "class", "type",
317 "cellspacing", "cellpadding",
318 "href", "align", "valign", "name", "lang",
319 "value", "action", "width", "height",
320 "content", "http-equiv", "src", "alt",
321 "bgcolor", "text", "link", "vlink", "alink",
325 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
326 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/" /* single-quoted strings */
329 [ '/<!--/', '/-->/' ]
337 'name' => 'Config file',
338 'filenames' => [ '/\.conf$/', '/\.cfg$/', '/^.*\/?\.[0-9a-z_-]+rc$/' ],
340 'word1', [ '/^\[.+\]$/m' ],
341 'word2', [ "/^[^:=\n]+[:=]/m" ],
343 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
344 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
345 "/\b-?[0-9]+\b/", /* numbers */
346 "/\b0x[0-9a-f]+\b/i" /* hex numbers */
348 'comments', [ "/#.*$/m" ]
356 'filenames' => [ '/\.php[345]?$/', '/\.inc$/' ],
360 "and", "array", "as",
361 "bool", "boolean", "break", "case", "class",
362 "const", "continue", "declare", "default",
363 "die", "do", "double", "echo", "else", "elseif",
364 "empty", "enddeclare", "endfor", "endforeach",
365 "endif", "endswitch", "endwhile", "eval",
366 "exit", "extends", "__FILE__", "float", "for",
367 "foreach", "function", "cfunction", "global",
368 "if", "include", "include_once", "int",
369 "integer", "isset", "__LINE__", "list", "new",
370 "object", "old_function", "or", "print", "real",
371 "require", "require_once", "return",
372 "static", "string", "switch", "unset", "use",
373 "var", "while", "xor", 'true', 'false' ]
376 'word2', [ '/\$\w+/', '/[-=<>:\?\+\*\!\%&\|]+/' ],
378 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
379 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
380 "/\b-?[0-9]+\b/", /* numbers */
381 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
382 "/\b[0-9[:upper:]_]+\b/", /* all-caps words */
383 [ "/`/", "/`/" ] /* backticks */
386 [ '|/\*|', '|\*/|' ], /* C-like */
395 'filenames' => [ '/\.py$/' ],
399 "and", "assert", "break", "class", "continue",
400 "def", "del", "elif", "else", "except", "exec",
401 "finally", "for", "from", "if", "import", "in",
402 "is", "lambda", "not", "or", "pass", "print",
403 "raise", "return", "try", "while", "yield"
407 'word2', [ '/global/', '/\$\w+/', '/[-=<>:\?\+\*\!\%&\|{}]+/' ],
409 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
410 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
411 "/\b-?[0-9]+\b/", /* numbers */
412 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
413 "/\b[0-9[:upper:]_]+\b/", /* all-caps words */
414 [ "/`/", "/`/" ] /* backticks */
416 'comments', [ "/#.*$/m", [ '/""".+[^"]$/m', '/"""$/m' ] ]
418 'detect' => sub (d) {
419 /* take the first line */
420 local f = d.txt.lines[0];
422 /* is it a 'she-bang'? */
423 return regex('/^#!\s*/usr/bin/(env )?python/', f);
430 'filenames' => [ '/\.rb$/' ],
434 "BEGIN", "END", "alias", "and", "begin",
435 "break", "case", "class", "def", "defined",
436 "do", "else", "elsif", "end", "ensure",
437 "false", "for", "if", "in", "module", "next",
438 "nil", "not", "or", "redo", "rescue", "retry",
439 "return", "self", "super", "then", "true",
440 "undef", "unless", "until", "when", "while",
441 "yield", "require", "include" ]
444 'word2', [ '/[-=<>:\?\+\*\!\%&\|{}]+/', '/=(begin|end)/' ],
446 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
447 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
448 "/\b-?[0-9]+\b/", /* numbers */
449 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
450 "/\b[0-9[:upper:]_]+\b/", /* all-caps words */
451 [ "/`/", "/`/" ] /* backticks */
453 'comments', [ "/#.*$/m" ]
455 'detect' => sub (d) {
456 /* take the first line */
457 local f = d.txt.lines[0];
459 /* is it a 'she-bang'? */
460 return regex('/^#!\s*/usr/bin/(env )?ruby/', f);
467 'filenames' => [ '/\.diff$/', '/\.patch$/' ],
469 'word1', [ '/^\+.+$/m' ],
470 'word2', [ '/^\-.+$/m' ],
471 'quotes', [ '/^@@.+@@$/m' ]
475 mp.syntax.commit_msg = {
476 'id' => 'commit_msg',
477 'name' => 'VCS commit message',
478 'filenames' => [ '/sv[kn]-commit.*\./' ],
480 'word1', [ '/^AM? .+$/m' ],
481 'word2', [ '/^D .+$/m' ],
482 'quotes', [ '/^M .+$/m' ],
483 'comments', [ '/^=== .+$/m', '/^--.+$/m' ]
489 'name' => 'Gettext file',
490 'filenames' => [ '/\.po$/' ],
492 'word1', [ '/^msgid/m' ],
493 'word2', [ '/^msgstr/m' ],
494 'comments', [ "/#.*$/m" ],
496 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/" /* double-quoted strings */
503 'name' => 'XML / XGML',
504 'filenames' => [ '/\.xml$/', '/\.sgml$/' ],
506 'word1', [ '/<[^>]+>/' ],
507 'word2', [ '/<\?[^\?]+\?>/' ],
509 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
510 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/" /* single-quoted strings */
513 [ '/<!--/', '/-->/' ]
516 'detect' => sub (d) {
517 /* take the first line */
518 local f = d.txt.lines[0];
520 return regex('/<\?xml/', f);
524 mp.syntax.make_output = {
525 'id' => 'make_output',
526 'name' => 'Make output',
528 'word1', [ "/^.*warning:.*$/m" ],
529 'word2', [ "/^.*error:.*$/m" ]
533 mp.syntax.euphoria = {
535 'name' => 'euphoria',
536 'filenames' => [ '/\.e$/', '/\.eu$/', '/\.ew$/', '/\.ed$/',
537 '/\.ex$/', '/\.exw$/', '/\.exu$/' ],
541 "as", "and", "break", "by", "case", "constant", "continue", "do", "end",
542 "else", "elsif", "elsifdef",
543 "exit", "entry", "enum", "export", "for", "function", "global", "include",
544 "if", "ifdef", "label",
545 "not", "or", "procedure", "return", "retry", "switch", "then", "type",
546 "to", "while", "with", "without", "xor"
551 "atom", "integer", "sequence", "object"
555 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
556 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
557 "/\b-?#[0-9A-F]+\b/", /* hex numbers */
558 "/\b-?[0-9]+\b/", /* numbers */
559 "/\b[0-9[:upper:]_]+\b/" /* all-caps words */
561 'comments', [ "/--.*$/m" ]
565 sub mp.detect_syntax(doc)
566 /* tries to detect the syntax of a document */
570 /* loops the syntax highlight definitions */
571 foreach (n, keys(mp.syntax)) {
572 local s = mp.syntax[n];
574 /* test the extensions */
575 foreach (ext, s.filenames) {
576 if (regex(ext, doc.name)) {
583 /* not by extension? try the 'detect' subroutine */
584 foreach (n, keys(mp.syntax)) {
585 local s = mp.syntax[n];
587 if (is_exec(s.detect) && s.detect(doc)) {
595 sub mp.help(doc, word)
599 foreach (c, doc.syntax.help) {
602 /* format the command */
603 c = sprintf(c, word);
606 if ((f = popen(c, "r")) != NULL) {
610 while ((l = read(f)) != NULL)
611 push(h, mp.chomp(l));
618 /* is there already help? don't look for more */