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
28 /** editor actions **/
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 mp.actions['section_list'] = sub (d) {
52 local l = mp.section_list(d);
55 mp.alert(L("No detection for sections for this mode."));
58 mp.alert(L("No sections were found in this document."));
62 'label' => L("Section list"),
64 'list' => map(sub (e) { e[0]; }, l),
70 mp.set_y(d, l[r[0]][1]);
77 /** default key bindings **/
79 mp.keycodes['f1'] = "help";
80 mp.keycodes['ctrl-d'] = "section_list";
82 /** action descriptions **/
84 mp.actdesc['help'] = LL("Help on word over cursor");
85 mp.actdesc['section_list'] = LL("Section list...");
91 /** syntax definitions **/
93 sub mp.syn_token_list(l) { '/\b(' ~ join("|", l) ~ ')\b/'; }
98 'filenames' => [ '/\.c$/', '/\.h$/', '/\.l$/', '/\.y$/', '/\.d$/',
99 '/\.cpp$/', '/\.hpp$/', '/\.c++$/', '/\.xpm$/' ],
100 'help' => [ "man 2 %s", "man 3 %s" ],
104 "for", "while", "if", "switch", "case", "do",
105 "else", "break", "continue", "return",
106 "default", "goto", "main", "fopen", "fclose",
107 "fgets", "fgetc", "fputs", "fputc", "fprintf",
108 "putc", "printf", "sprintf", "strcpy", "strcat",
109 "strcmp", "strncmp", "strtok", "stricmp", "strchr",
110 "strrchr", "strlen", "memcmp", "memcpy", "malloc",
111 "free", "strncpy", "strncat", "snprintf", "strstr",
112 "memset", "memcpy", "va_start", "va_end", "vsprintf",
113 "vsnprintf", "atoi", "qsort", "bsearch", "getenv",
114 "fscanf", "popen", "pclose", "realloc", "fread",
115 "fwrite", "fseek", '\{', '\}', "putchar", "fflush",
116 "wcscmp", "swprintf", "wmemcpy", "swscanf", "sscanf",
117 "wcslen", "wmemset", "wcscpy", "wcsncpy", "wcscoll",
118 "mbstowcs", "wcstombs", "wprintf", "wcschr",
119 "wcsrchr", "wcsstr", "strdup", "wctomb", "mbtowc",
120 "mbrtowc", "wcrtomb", "open", "close", "read",
121 "write", "pipe", "fork", "dup", "dup2", "wait",
122 "execl", "execlp", "execle", "execv", "execvp"
127 "char", "int", "long", "struct", "union", "const",
128 "void", "unsigned", "signed", "auto", "volatile",
129 "enum", "typedef", "float", "double", "extern",
130 "register", "short", "sizeof", "static", "far",
131 "near", "defined", "va_list", "size_t", "wchar_t",
133 '/[-=<>:\?\+\*\/\!\%&\|]+/'
136 /* from http://ad.hominem.org/log/2005/05/quoted_strings.php */
137 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
138 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
139 "/\b-?[0-9]+\b/", /* numbers */
140 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
141 "/\b[0-9[:upper:]_]+\b/" /* all-caps words */
144 [ '|/\*|', '|\*/|' ], /* C-like */
145 '|//.*$|m', /* C++ */
146 '/^\s*#[a-z]+/m' /* CPP directives */
149 [ "|/\*\*\n|", '|\*/|' ], /* mp_doccer */
150 '/^/\*\*.*\*\*/$/m' /* section mark */
153 'section' => '/(^\/\*\*.*\*\*\/$|^#pragma mark)/'
158 'name' => 'Resource file',
159 'filenames' => [ '/\.rc$/' ],
164 /* from http://ad.hominem.org/log/2005/05/quoted_strings.php */
165 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
166 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
167 "/\b-?[0-9]+\b/", /* numbers */
168 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
169 "/\b[0-9[:upper:]_]+\b/" /* all-caps words */
172 [ '|/\*|', '|\*/|' ], /* C-like */
173 '|//.*$|m', /* C++ */
174 '/^\s*#[a-z]+/m' /* CPP directives */
182 'filenames' => [ '/\.pl$/i', '/\.pm$/' ],
183 'help' => [ 'perldoc -f %s', 'perldoc %s' ],
184 'help_word_regex' => '/[A-Z_][A-Z0-9_:]*/i',
188 "for", "if", "next", "last", "else", "elsif",
189 "unless", "while", "shift", "unshift", "push",
190 "pop", "delete", "new", "bless", "return",
191 "foreach", "keys", "values", "sort", "grep",
192 "tr", "length", "system", "exec", "fork", "map",
193 "print", "write", "open", "close", "chop",
194 "chomp", "exit", "sleep", "split", "join",
195 "sub", "printf", "sprintf", "s", "glob",
196 "scalar", "my", "local", "undef", "defined",
197 "use", "package", "require", "ref", "can", "isa",
198 "qw", "qq", "eq", "ne", "or", "exists",
199 "and", "not", "import", "our", "caller" ]),
203 '/[:\?\+\*\/\!\$@\%&\|~\.]+/',
207 /* from http://ad.hominem.org/log/2005/05/quoted_strings.php */
208 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
209 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
210 "/\b-?[0-9]+\b/", /* numbers */
211 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
212 "/\b[0-9[:upper:]_]+\b/", /* all-caps words */
213 [ "/q\(/", "/\)/" ], /* quote */
214 [ "/qw\(/", "/\)/" ], /* quote word */
215 '/\w+\s*=>/', /* barewords as hash keys 1 */
216 '/\{\s*-?\w+\s*\}/', /* barewords as hash keys 2 */
217 [ "/`/", "/`/" ], /* backticks */
218 [ "/<<[\"']?EOF.*$/m", "/^EOF$/m" ] /* 'document here' */
220 /* color all => as word2 */
222 /* color curly brackets as word */
223 'word', [ '/[{}]/' ],
225 "/#.*$/m" /* Comments */
228 "/__END__\n.*$/", /* __END__ */
229 '/^## .*$/m', /* section mark */
230 [ "/^=(head[1-4]|over|item|back|pod|begin|end|for)/m",
231 "/^=cut$/m" ] /* POD */
234 'detect' => sub (d) {
235 /* take the first line */
236 local f = d.txt.lines[0];
238 /* is it a 'she-bang' for Perl? */
239 return regex('/^#!\s*/usr/bin/(env )?perl/', f);
241 'section' => '/(^sub \w+|^package|^## )/'
248 'filenames' => [ '/\.mpsl$/' ],
252 'if', 'else', 'while', 'foreach',
253 'sub', 'break', 'return', 'eq', 'ne' ]),
254 mp.syn_token_list( keys(MPSL.CORE) ),
259 '/[-=<>:\?\+\*\/\!\%&\|]+/'
262 /* from http://ad.hominem.org/log/2005/05/quoted_strings.php */
263 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
264 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
265 "/\b-?[0-9]+\b/", /* numbers */
266 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
267 "/\b[0-9[:upper:]_]+\b/" /* all-caps words */
270 [ '|/\*|', '|\*/|' ] /* C-like */
273 [ "|/\*\*\n|", '|\*/|' ], /* mp_doccer */
274 '/^/\*\*.*\*\*/$/m' /* section mark */
277 'section' => '/(^sub \w+|^\/\*\*.*\*\*\/$)/'
282 'name' => 'Shell script',
283 'filenames' => [ '/\.sh$/', '/makefile/i' ],
287 "if", "then", "else", "elif",
288 "fi", "case", "do", "done", "esac",
289 "for", "until", "while", "break",
290 "in", "source", "alias", "cd",
291 "continue", "echo", "eval", "exec",
292 "exit", "export", "kill", "logout",
293 "printf", "pwd", "read", "return",
294 "shift", "test", "trap", "ulimit",
295 "umask", "unset", "wait", "cp", "rm" ]),
300 '/\b(local|let|set)\b/',
301 '/[-=<>:\?\+\*\!\%&\|]+/',
304 "/\b[0-9[:upper:]_]+\b/" /* all-caps words */
307 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
308 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
309 "/\b-?[0-9]+\b/", /* numbers */
310 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
311 "/\([A-Za-z0-9_]+\)/", /* parens */
312 [ "/`/", "/`/" ], /* backticks */
313 [ "/<<[\"']?EOF[\"']?;$/m", "/^EOF$/m" ] /* 'document here' */
315 'comments', [ "/#.*$/m" ]
317 'detect' => sub (d) {
318 /* take the first line */
319 local f = d.txt.lines[0];
321 /* is it a 'she-bang' for usual shells? */
322 return regex('/^#!\s*/bin/(sh|bash|csh|dash|ksh)/', f) ||
323 regex('/^#!\s*/usr/bin/make -f/', f);
325 'section' => '/(^\w+\(\))/'
332 'filenames' => [ '/\.html$/', '/\.htm$/' ],
337 "a", "abbr", "acronym", "address",
338 "area", "b", "base", "bdo", "big",
339 "blockquote", "body", "br", "button",
340 "caption", "center", "cite", "code", "col",
341 "colgroup", "dd", "del", "dfn", "div",
342 "dl", "dt", "em", "fieldset", "form",
343 "h1", "h2", "h3", "h4", "h5", "h6",
344 "head", "hr", "html", "i", "img",
345 "input", "ins", "kbd", "label", "legend",
346 "li", "link", "map", "meta", "noscript",
347 "object", "ol", "optgroup", "option",
348 "p", "param", "pre", "q", "samp",
349 "script", "select", "small", "span",
350 "strong", "style", "sub", "sup", "table",
351 "tbody", "td", "textarea", "tfoot", "th",
352 "thead", "title", "tr", "tt", "ul",
358 "!DOCTYPE", "class", "type",
359 "cellspacing", "cellpadding",
360 "href", "align", "valign", "name", "lang",
361 "value", "action", "width", "height",
362 "content", "http-equiv", "src", "alt",
363 "bgcolor", "text", "link", "vlink", "alink",
367 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
368 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/" /* single-quoted strings */
371 [ '/<!--/', '/-->/' ]
379 'name' => 'Config file',
380 'filenames' => [ '/\.conf$/', '/\.cfg$/', '/^.*\/?\.[0-9a-z_-]+rc$/' ],
382 'word1', [ '/^\[.+\]$/m' ],
383 'word2', [ "/^[^:=\n]+[:=]/m" ],
385 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
386 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
387 "/\b-?[0-9]+\b/", /* numbers */
388 "/\b0x[0-9a-f]+\b/i" /* hex numbers */
390 'comments', [ "/#.*$/m" ]
398 'filenames' => [ '/\.php[345]?$/', '/\.inc$/' ],
402 "and", "array", "as",
403 "bool", "boolean", "break", "case", "class",
404 "const", "continue", "declare", "default",
405 "die", "do", "double", "echo", "else", "elseif",
406 "empty", "enddeclare", "endfor", "endforeach",
407 "endif", "endswitch", "endwhile", "eval",
408 "exit", "extends", "__FILE__", "float", "for",
409 "foreach", "function", "cfunction", "global",
410 "if", "include", "include_once", "int",
411 "integer", "isset", "__LINE__", "list", "new",
412 "object", "old_function", "or", "print", "real",
413 "require", "require_once", "return",
414 "static", "string", "switch", "unset", "use",
415 "var", "while", "xor", 'true', 'false' ]
418 'word2', [ '/\$\w+/', '/[-=<>:\?\+\*\!\%&\|]+/' ],
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 "/\b[0-9[:upper:]_]+\b/", /* all-caps words */
425 [ "/`/", "/`/" ] /* backticks */
428 [ '|/\*|', '|\*/|' ], /* C-like */
437 'filenames' => [ '/\.py$/' ],
441 "and", "assert", "break", "class", "continue",
442 "def", "del", "elif", "else", "except", "exec",
443 "finally", "for", "from", "if", "import", "in",
444 "is", "lambda", "not", "or", "pass", "print",
445 "raise", "return", "try", "while", "yield"
449 'word2', [ '/global/', '/\$\w+/', '/[-=<>:\?\+\*\!\%&\|{}]+/' ],
451 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
452 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
453 "/\b-?[0-9]+\b/", /* numbers */
454 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
455 "/\b[0-9[:upper:]_]+\b/", /* all-caps words */
456 [ "/`/", "/`/" ] /* backticks */
458 'comments', [ "/#.*$/m" ],
459 'documentation', [ [ '/""".+[^"]$/m', '/"""$/m' ] ]
461 'detect' => sub (d) {
462 /* take the first line */
463 local f = d.txt.lines[0];
465 /* is it a 'she-bang'? */
466 return regex('/^#!\s*/usr/bin/(env )?python/', f);
468 'section' => '/^[ \t]*def/'
474 'filenames' => [ '/\.rb$/' ],
478 "BEGIN", "END", "alias", "and", "begin",
479 "break", "case", "class", "def", "defined",
480 "do", "else", "elsif", "end", "ensure",
481 "false", "for", "if", "in", "module", "next",
482 "nil", "not", "or", "redo", "rescue", "retry",
483 "return", "self", "super", "then", "true",
484 "undef", "unless", "until", "when", "while",
485 "yield", "require", "include" ]
488 'word2', [ '/[-=<>:\?\+\*\!\%&\|{}]+/', '/=(begin|end)/' ],
490 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
491 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
492 "/\b-?[0-9]+\b/", /* numbers */
493 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
494 "/\b[0-9[:upper:]_]+\b/", /* all-caps words */
495 [ "/`/", "/`/" ] /* backticks */
497 'comments', [ "/#.*$/m" ]
499 'detect' => sub (d) {
500 /* take the first line */
501 local f = d.txt.lines[0];
503 /* is it a 'she-bang'? */
504 return regex('/^#!\s*/usr/bin/(env )?ruby/', f);
511 'filenames' => [ '/\.diff$/', '/\.patch$/' ],
513 'word1', [ '/^\+.+$/m' ],
514 'word2', [ '/^\-.+$/m' ],
515 'quotes', [ '/^@@.+@@$/m' ]
517 'section' => '/^--- '
520 mp.syntax.commit_msg = {
521 'id' => 'commit_msg',
522 'name' => 'VCS commit message',
523 'filenames' => [ '/sv[kn]-commit.*\./' ],
525 'word1', [ '/^AM? .+$/m' ],
526 'word2', [ '/^D .+$/m' ],
527 'quotes', [ '/^M .+$/m' ],
528 'comments', [ '/^=== .+$/m', '/^--.+$/m' ]
534 'name' => 'Gettext file',
535 'filenames' => [ '/\.po$/' ],
537 'word1', [ '/^msgid/m' ],
538 'word2', [ '/^msgstr/m' ],
539 'comments', [ "/#.*$/m" ],
541 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/" /* double-quoted strings */
548 'name' => 'XML / XGML',
549 'filenames' => [ '/\.xml$/', '/\.sgml$/' ],
551 'word1', [ '/<[^>]+>/' ],
552 'word2', [ '/<\?[^\?]+\?>/' ],
554 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
555 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/" /* single-quoted strings */
558 [ '/<!--/', '/-->/' ]
561 'detect' => sub (d) {
562 /* take the first line */
563 local f = d.txt.lines[0];
565 return regex('/<\?xml/', f);
569 mp.syntax.make_output = {
570 'id' => 'make_output',
571 'name' => 'Make output',
573 'word1', [ "/^.*warning:.*$/m" ],
574 'word2', [ "/^.*error:.*$/m" ]
578 mp.syntax.euphoria = {
580 'name' => 'euphoria',
581 'filenames' => [ '/\.e$/', '/\.eu$/', '/\.ew$/', '/\.ed$/',
582 '/\.ex$/', '/\.exw$/', '/\.exu$/' ],
586 "as", "and", "break", "by", "case", "constant", "continue", "do", "end",
587 "else", "elsif", "elsifdef",
588 "exit", "entry", "enum", "export", "for", "function", "global", "include",
589 "if", "ifdef", "label",
590 "not", "or", "procedure", "return", "retry", "switch", "then", "type",
591 "to", "while", "with", "without", "xor"
596 "atom", "integer", "sequence", "object"
600 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
601 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
602 "/\b-?#[0-9A-F]+\b/", /* hex numbers */
603 "/\b-?[0-9]+\b/", /* numbers */
604 "/\b[0-9[:upper:]_]+\b/" /* all-caps words */
606 'comments', [ "/--.*$/m" ]
608 'section' => '/^[ \t]*(global|export)*[ \t]*(function|procedure)/'
614 sub mp.detect_syntax(doc)
615 /* tries to detect the syntax of a document */
619 /* loops the syntax highlight definitions */
620 foreach (n, keys(mp.syntax)) {
621 local s = mp.syntax[n];
623 /* test the extensions */
624 foreach (ext, s.filenames) {
625 if (regex(ext, doc.name)) {
632 /* not by extension? try the 'detect' subroutine */
633 foreach (n, keys(mp.syntax)) {
634 local s = mp.syntax[n];
636 if (is_exec(s.detect) && s.detect(doc)) {
644 sub mp.help(doc, word)
648 foreach (c, doc.syntax.help) {
651 /* format the command */
652 c = sprintf(c, word);
655 if ((f = popen(c, "r")) != NULL) {
659 while ((l = read(f)) != NULL)
660 push(h, mp.chomp(l));
667 /* is there already help? don't look for more */
677 * mp.section_list - Returns the list of sections of a document.
680 * Applies the `section' regular expression of the document's
681 * syntax definition and returns it as an array of line and
684 * If the document has no syntax highlight definition, or it has
685 * one without a `section' regex, NULL is returned. Otherwise,
686 * an array of line, line number pairs is returned (it can be
687 * an empty list if the document has no recognizable sections).
689 sub mp.section_list(doc)
693 if (doc.syntax.section) {
698 while ((l = doc.txt.lines[n]) != NULL) {
699 if (regex(doc.syntax.section, l))