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) {
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 sub mp.actions.section_list(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 /* set pos to the section nearest the cursor */
70 if (pos < 0 || pos > size(l))
76 label: L("Section list"),
78 list: map(l, sub (e) { e[0]; }),
85 mp.search_set_y(d, l[r[0]][1]);
92 /** default key bindings **/
94 mp.keycodes['f1'] = "help";
95 mp.keycodes['ctrl-d'] = "section_list";
97 /** action descriptions **/
99 mp.actdesc['help'] = LL("Help on word over cursor");
100 mp.actdesc['section_list'] = LL("Section list...");
106 /** syntax definitions **/
108 sub mp.syn_token_list(l) { '/\b(' ~ join(l, "|") ~ ')\b/'; }
113 'filenames' => [ '/\.c$/i', '/\.h$/i', '/\.l$/i', '/\.y$/i', '/\.d$/i',
114 '/\.cpp$/i', '/\.hpp$/i', '/\.c++$/i', '/\.cxx$/i', '/\.xpm$/i' ],
115 'help' => [ "man 2 %s", "man 3 %s" ],
119 "auto", "break", "case", "catch", "class", "const", "const_cast",
120 "continue", "default", "delete", "do", "dynamic_cast", "else", "enum",
121 "explicit", "extern", "for", "friend", "goto", "if", "inline", "mutable",
122 "namespace", "new", "operator", "private", "protected", "public",
123 "register", "reinterpret_cast", "restrict", "return", "sizeof", "static",
124 "static_cast", "struct", "switch", "template", "this", "throw", "try",
125 "typedef", "typeid", "typename", "union", "using", "virtual", "volatile",
126 "while", "not", "not_eq", "xor", "xor_eq", "or", "or_eq", "and", "and_eq",
127 "bitor", "bitand", "compl"
132 "bool", "char", "double", "float", "int", "long", "short", "signed",
133 "unsigned", "wchar_t", "size_t", "ssize_t", "off_t", "wchar_t",
134 "ptrdiff_t", "void", "sig_atomic_t", "fpos_t", "clock_t", "time_t",
135 "va_list", "jmp_buf", "FILE", "DIR", "div_t", "ldiv_t", "mbstate_t",
136 "wctrans_t", "wint_t", "wctype_t", "complex", "int8_t", "int16_t",
137 "int32_t", "int64_t", "uint8_t", "uint16_t", "uint32_t", "uint64_t",
138 "int_least8_t", "int_least16_t", "int_least32_t", "int_least64_t",
139 "uint_least8_t", "uint_least16_t", "uint_least32_t", "uint_least64_t",
140 "int_fast8_t", "int_fast16_t", "int_fast32_t", "int_fast64_t",
141 "uint_fast8_t", "uint_fast16_t", "uint_fast32_t", "uint_fast64_t",
142 "intptr_t", "uintptr_t", "intmax_t", "uintmax_t"
144 '/[-=<>:\?\+\*\/\!\%&\|~\^\.]+/'
147 /* from http://ad.hominem.org/log/2005/05/quoted_strings.php */
148 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
149 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
150 "/\b-?[0-9]+\b/", /* numbers */
151 "/\b[0-9]+e[0-9]+\b/", /* numbers in exp format */
152 "/\b0[0-7]+\b/", /* octal numbers */
153 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
154 "/\b__(DATE|FILE|LINE|STDC|TIME|STDC_HOSTED|STDC_VERSION|func)__\b/", /* ISO macros */
155 "/\b(true|false|NULL)\b/" /* symbolic constant names */
158 [ '|/\*|', '|\*/|' ], /* C-like */
159 [ '|#if 0|', '#endif' ], /* CPP "comments" */
160 '|//.*$|m', /* C++ */
161 '/^\s*#\s*[a-z]+/m' /* CPP directives */
164 [ "|/\*\*\n|", '|\*/|' ], /* mp_doccer */
165 '/^/\*\*.*\*\*/$/m' /* section mark */
168 'section' => [ '/(^\/\*\*.*\*\*\/$|^#pragma mark|^[A-Za-z_])/' ]
173 'name' => 'Resource file',
174 'filenames' => [ '/\.rc$/i' ],
179 /* from http://ad.hominem.org/log/2005/05/quoted_strings.php */
180 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
181 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
182 "/\b-?[0-9]+\b/", /* numbers */
183 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
184 "/\b[0-9[:upper:]_]+\b/" /* all-caps words */
187 [ '|/\*|', '|\*/|' ], /* C-like */
188 '|//.*$|m', /* C++ */
189 '/^\s*#[a-z]+/m' /* CPP directives */
197 'filenames' => [ '/\.pl$/i', '/\.pm$/i' ],
198 'help' => [ 'perldoc -f %s', 'perldoc %s' ],
199 'help_word_regex' => '/[A-Z_][A-Z0-9_:]*/i',
203 "for", "if", "next", "last", "else", "elsif",
204 "unless", "while", "shift", "unshift", "push",
205 "pop", "delete", "new", "bless", "return",
206 "foreach", "keys", "values", "sort", "grep",
207 "tr", "length", "system", "exec", "fork", "map",
208 "print", "write", "open", "close", "chop",
209 "chomp", "exit", "sleep", "split", "join",
210 "sub", "printf", "sprintf", "s", "glob",
211 "scalar", "my", "local", "undef", "defined",
212 "use", "package", "require", "ref", "can", "isa",
213 "qw", "qq", "eq", "ne", "or", "exists",
214 "and", "not", "import", "our", "caller" ]),
218 '/[:\?\+\*\/\!\$@\%&\|~\.]+/',
222 /* from http://ad.hominem.org/log/2005/05/quoted_strings.php */
223 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
224 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
225 "/\b-?[0-9]+\b/", /* numbers */
226 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
227 "/\b[0-9[:upper:]_]+\b/", /* all-caps words */
228 [ "/q\(/", "/\)/" ], /* quote */
229 [ "/qw\(/", "/\)/" ], /* quote word */
230 '/\w+\s*=>/', /* barewords as hash keys 1 */
231 '/\{\s*-?\w+\s*\}/', /* barewords as hash keys 2 */
232 [ "/`/", "/`/" ], /* backticks */
233 [ "/<<[\"']?EOF.*$/m", "/^EOF$/m" ] /* 'document here' */
235 /* color all => as word2 */
237 /* color curly brackets as word */
238 'word', [ '/[{}]/' ],
240 "/#.*$/m" /* Comments */
243 "/__END__\n.*$/", /* __END__ */
244 '/^## .*$/m', /* section mark */
245 [ "/^=(head[1-4]|over|item|back|pod|begin|end|for)/m",
246 "/^=cut$/m" ] /* POD */
249 'detect' => sub (d) {
250 /* take the first line */
251 local f = d.txt.lines[0];
253 /* is it a 'she-bang' for Perl? */
254 return regex(f, '/^#!\s*/usr/bin/(env )?perl/');
256 'section' => [ '/(^sub \w+|^package|^## )/' ]
263 'filenames' => [ '/\.mpsl?$/i' ],
267 'if', 'else', 'while', 'foreach',
268 'sub', 'break', 'return', 'eq', 'ne' ]),
269 mp.syn_token_list( keys(MPSL.CORE) ),
274 '/[-=<>\?\+\*\/\!\%&\|]+/'
277 /* from http://ad.hominem.org/log/2005/05/quoted_strings.php */
278 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
279 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
280 "/\b-?[0-9]+\b/", /* numbers */
281 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
282 '/\w+\s*:/', /* JS-type hashes */
283 "/\b[0-9[:upper:]_]+\b/" /* all-caps words */
289 [ '|/\*|', '|\*/|' ] /* C-like */
292 [ "|/\*\*\n|", '|\*/|' ], /* mp_doccer */
293 '/^/\*\*.*\*\*/$/m' /* section mark */
296 'section' => [ '/(^[A-Za-z_]|^\/\*\*.*\*\*\/$)/' ]
301 'name' => 'Shell script',
302 'filenames' => [ '/\.sh$/i', '/makefile/i' ],
306 "if", "then", "else", "elif",
307 "fi", "case", "do", "done", "esac",
308 "for", "until", "while", "break",
309 "in", "source", "alias", "cd",
310 "continue", "echo", "eval", "exec",
311 "exit", "export", "kill", "logout",
312 "printf", "pwd", "read", "return",
313 "shift", "test", "trap", "ulimit",
314 "umask", "unset", "wait", "cp", "rm" ]),
319 '/\b(local|let|set)\b/',
320 '/[-=<>:\?\+\*\!\%&\|]+/',
323 "/\b[0-9[:upper:]_]+\b/" /* all-caps words */
326 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
327 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
328 "/\b-?[0-9]+\b/", /* numbers */
329 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
330 "/\([A-Za-z0-9_]+\)/", /* parens */
331 [ "/`/", "/`/" ], /* backticks */
332 [ "/<<[\"']?EOF[\"']?;$/m", "/^EOF$/m" ] /* 'document here' */
334 'comments', [ "/#.*$/m" ]
336 'detect' => sub (d) {
337 /* take the first line */
338 local f = d.txt.lines[0];
340 /* is it a 'she-bang' for usual shells? */
341 return regex(f, '/^#!\s*/bin/(sh|bash|csh|dash|ksh)/') ||
342 regex(f, '/^#!\s*/usr/bin/make -f/');
344 'section' => [ '/(^\w+\(\))/', '/^[A-Za-z0-9_\.-]+:/' ]
351 'filenames' => [ '/\.html$/i', '/\.htm$/i' ],
356 "a", "abbr", "acronym", "address",
357 "area", "b", "base", "bdo", "big",
358 "blockquote", "body", "br", "button",
359 "caption", "center", "cite", "code", "col",
360 "colgroup", "dd", "del", "dfn", "div",
361 "dl", "dt", "em", "fieldset", "form",
362 "h1", "h2", "h3", "h4", "h5", "h6",
363 "head", "hr", "html", "i", "img",
364 "input", "ins", "kbd", "label", "legend",
365 "li", "link", "map", "meta", "noscript",
366 "object", "ol", "optgroup", "option",
367 "p", "param", "pre", "q", "samp",
368 "script", "select", "small", "span",
369 "strong", "style", "sub", "sup", "table",
370 "tbody", "td", "textarea", "tfoot", "th",
371 "thead", "title", "tr", "tt", "ul",
377 "!DOCTYPE", "class", "type",
378 "cellspacing", "cellpadding",
379 "href", "align", "valign", "name", "lang",
380 "value", "action", "width", "height",
381 "content", "http-equiv", "src", "alt",
382 "bgcolor", "text", "link", "vlink", "alink",
386 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
387 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/" /* single-quoted strings */
390 [ '/<!--/', '/-->/' ]
393 'section' => [ '/<\s*h[0-9]\s*>/' ]
399 'name' => 'Config file',
400 'filenames' => [ '/\.conf$/i', '/\.cfg$/i', '/^.*\/?\.[0-9a-z_-]+rc$/i' ],
402 'word1', [ '/^\[.+\]$/m' ],
403 'word2', [ "/^[^:=\n]+[:=]/m" ],
405 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
406 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
407 "/\b-?[0-9]+\b/", /* numbers */
408 "/\b0x[0-9a-f]+\b/i" /* hex numbers */
410 'comments', [ "/#.*$/m" ]
418 'filenames' => [ '/\.php[345]?$/i', '/\.inc$/i' ],
422 "and", "array", "as",
423 "bool", "boolean", "break", "case", "class",
424 "const", "continue", "declare", "default",
425 "die", "do", "double", "echo", "else", "elseif",
426 "empty", "enddeclare", "endfor", "endforeach",
427 "endif", "endswitch", "endwhile", "eval",
428 "exit", "extends", "__FILE__", "float", "for",
429 "foreach", "function", "cfunction", "global",
430 "if", "include", "include_once", "int",
431 "integer", "isset", "__LINE__", "list", "new",
432 "object", "old_function", "or", "print", "real",
433 "require", "require_once", "return",
434 "static", "string", "switch", "unset", "use",
435 "var", "while", "xor", 'true', 'false' ]
438 'word2', [ '/\$\w+/', '/[-=<>:\?\+\*\!\%&\|]+/' ],
440 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
441 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
442 "/\b-?[0-9]+\b/", /* numbers */
443 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
444 "/\b[0-9[:upper:]_]+\b/", /* all-caps words */
445 [ "/`/", "/`/" ] /* backticks */
448 [ '|/\*|', '|\*/|' ], /* C-like */
457 'filenames' => [ '/\.py$/i' ],
461 "and", "assert", "break", "class", "continue",
462 "def", "del", "elif", "else", "except", "exec",
463 "finally", "for", "from", "if", "import", "in",
464 "is", "lambda", "not", "or", "pass", "print",
465 "raise", "return", "try", "while", "yield"
469 'word2', [ '/global/', '/\$\w+/', '/[-=<>:\?\+\*\!\%&\|{}]+/' ],
471 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
472 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
473 "/\b-?[0-9]+\b/", /* numbers */
474 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
475 "/\b[0-9[:upper:]_]+\b/", /* all-caps words */
476 [ "/`/", "/`/" ] /* backticks */
478 'comments', [ "/#.*$/m" ],
479 'documentation', [ [ '/""".+[^"]$/m', '/"""$/m' ] ]
481 'detect' => sub (d) {
482 /* take the first line */
483 local f = d.txt.lines[0];
485 /* is it a 'she-bang'? */
486 return regex(f, '/^#!\s*/usr/bin/(env )?python/');
488 'section' => [ '/^[ \t]*def/' ]
494 'filenames' => [ '/\.rb$/i' ],
498 "BEGIN", "END", "alias", "and", "begin",
499 "break", "case", "class", "def", "defined",
500 "do", "else", "elsif", "end", "ensure",
501 "false", "for", "if", "in", "module", "next",
502 "nil", "not", "or", "redo", "rescue", "retry",
503 "return", "self", "super", "then", "true",
504 "undef", "unless", "until", "when", "while",
505 "yield", "require", "include" ]
508 'word2', [ '/[-=<>:\?\+\*\!\%&\|{}]+/', '/=(begin|end)/' ],
510 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
511 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
512 "/\b-?[0-9]+\b/", /* numbers */
513 "/\b0x[0-9a-f]+\b/i", /* hex numbers */
514 "/\b[0-9[:upper:]_]+\b/", /* all-caps words */
515 [ "/`/", "/`/" ] /* backticks */
517 'comments', [ "/#.*$/m" ]
519 'detect' => sub (d) {
520 /* take the first line */
521 local f = d.txt.lines[0];
523 /* is it a 'she-bang'? */
524 return regex(f, '/^#!\s*/usr/bin/(env )?ruby/');
531 'filenames' => [ '/\.diff$/i', '/\.patch$/i' ],
533 'word1', [ '/^\+.+$/m' ],
534 'word2', [ '/^\-.+$/m' ],
535 'quotes', [ '/^@@.+@@.*$/m' ]
537 'section' => [ '/^--- ', '/^@@/' ]
540 mp.syntax.commit_msg = {
541 'id' => 'commit_msg',
542 'name' => 'VCS commit message',
543 'filenames' => [ '/sv[kn]-commit.*\./' ],
545 'word1', [ '/^AM? .+$/m' ],
546 'word2', [ '/^D .+$/m' ],
547 'quotes', [ '/^M .+$/m' ],
548 'comments', [ '/^=== .+$/m', '/^--.+$/m' ]
554 'name' => 'Gettext file',
555 'filenames' => [ '/\.po$/i' ],
557 'word1', [ '/^msgid/m' ],
558 'word2', [ '/^msgstr/m' ],
559 'comments', [ "/#.*$/m" ],
561 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/" /* double-quoted strings */
568 'name' => 'XML / XGML',
569 'filenames' => [ '/\.xml$/i', '/\.sgml$/i' ],
571 'word1', [ '/<[^>]+>/' ],
572 'word2', [ '/<\?[^\?]+\?>/' ],
574 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
575 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/" /* single-quoted strings */
578 [ '/<!--/', '/-->/' ]
581 'detect' => sub (d) {
582 /* take the first line */
583 local f = d.txt.lines[0];
585 return regex(f, '/<\?xml/');
589 mp.syntax.make_output = {
590 'id' => 'make_output',
591 'name' => 'Make output',
593 'word1', [ "/^.*warning:.*$/m" ],
594 'word2', [ "/^.*error:.*$/m" ]
598 mp.syntax.euphoria = {
600 'name' => 'euphoria',
601 'filenames' => [ '/\.e$/', '/\.eu$/', '/\.ew$/', '/\.ed$/',
602 '/\.ex$/', '/\.exw$/', '/\.exu$/' ],
606 "as", "and", "break", "by", "case", "constant", "continue", "do", "end",
607 "else", "elsif", "elsifdef",
608 "exit", "entry", "enum", "export", "for", "function", "global", "include",
609 "if", "ifdef", "label",
610 "not", "or", "procedure", "return", "retry", "switch", "then", "type",
611 "to", "while", "with", "without", "xor"
616 "atom", "integer", "sequence", "object"
620 "/\"([^\"\\\\\n]*(\\\\.[^\"\\\\\n]*)*)\"/", /* double-quoted strings */
621 "/'([^'\\\\\n]*(\\\\.[^'\\\\\n]*)*)'/", /* single-quoted strings */
622 "/\b-?#[0-9A-F]+\b/", /* hex numbers */
623 "/\b-?[0-9]+\b/", /* numbers */
624 "/\b[0-9[:upper:]_]+\b/" /* all-caps words */
626 'comments', [ "/--.*$/m" ]
628 'section' => [ '/^[ \t]*(global|export)*[ \t]*(function|procedure)/' ]
632 mp.syntax.mp_templates = {
633 'id' => 'mp_templates',
634 'name' => 'Minimum Profit template file',
635 'filenames' => [ '/\.mp_templates$/' ],
637 'documentation', [ '/^%%.*$/m' ]
639 'section' => [ '/^%%/' ]
643 mp.syntax.hex_view = {
645 'name' => 'Hexadecimal view',
647 'word1', [ "/^\| [0-9A-F]+ \|/m" ],
653 mp.syntax.grutatxt = {
655 'name' => 'Grutatxt',
657 'word1', [ "/[^\n]+\n[-=~]+\n/", "/ \* [^.\n:]+:/" ],
658 'comments', [ "/\b_[^ \t\n][^_\n]*[^ \t\n]_\b/" ],
659 'word2', [ "/\*[^ \t\n][^\*\n]*[^ \t\n]\*/" ],
660 'quotes', [ "/`[^ \t\n][^'\n]*[^ \t\n]'/" ]
662 'section' => [ "/^[-=~]+$/" ],
663 'detect' => sub (d) {
666 /* search the first lines for a heading */
668 local l = d.txt.lines[n++];
673 if (regex(l, "/^[-=~]+$/"))
684 sub mp.detect_syntax(doc)
685 /* tries to detect the syntax of a document */
689 /* loops the syntax highlight definitions */
690 foreach (n, keys(mp.syntax)) {
691 local s = mp.syntax[n];
693 /* test the extensions */
694 foreach (ext, s.filenames) {
695 if (regex(doc.name, ext)) {
702 /* not by extension? try the 'detect' subroutine */
703 foreach (n, keys(mp.syntax)) {
704 local s = mp.syntax[n];
706 if (is_exec(s.detect) && s.detect(doc)) {
714 sub mp.help(doc, word)
718 foreach (c, doc.syntax.help) {
721 /* format the command */
722 c = sprintf(c, word);
725 if ((f = popen(c, "r")) != NULL) {
729 while ((l = read(f)) != NULL)
730 push(h, mp.chomp(l));
737 /* is there already help? don't look for more */
747 * mp.section_list - Returns the list of sections of a document.
750 * Applies the `section' array of regular expressions of the
751 * document's syntax definition and returns it as an array of
752 * line and line number pairs.
754 * If the document has no syntax highlight definition, or it has
755 * one without a `section' definition, NULL is returned. Otherwise,
756 * an array of line, line number pairs is returned (it can be
757 * an empty list if the document has no recognizable sections).
759 sub mp.section_list(doc)
763 if (doc.syntax.section) {
768 while ((l = doc.txt.lines[n]) != NULL) {
769 foreach (ex, doc.syntax.section) {