1 /* editor syntax highlighting.
3 Copyright (C) 1996, 1997, 1998 the Free Software Foundation
5 Authors: 1998 Paul Sheer
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
29 #define SYNTAX_MARKER_DENSITY 512
32 Mispelled words are flushed from the syntax highlighting rules
33 when they have been around longer than
34 TRANSIENT_WORD_TIME_OUT seconds. At a cursor rate of 30
35 chars per second and say 3 chars + a space per word, we can
36 accumulate 450 words absolute max with a value of 60. This is
37 below this limit of 1024 words in a context.
39 #define TRANSIENT_WORD_TIME_OUT 60
41 #define UNKNOWN_FORMAT "unknown"
45 int option_syntax_highlighting
= 1;
46 int option_auto_spellcheck
= 1;
48 /* these three functions are called from the outside */
49 void edit_load_syntax (WEdit
* edit
, char **names
, char *type
);
50 void edit_free_syntax_rules (WEdit
* edit
);
51 void edit_get_syntax_color (WEdit
* edit
, long byte_index
, int *fg
, int *bg
);
54 static void *mad_syntax_malloc (size_t x
, char *file
, int line
)
55 #define syntax_malloc(x) mad_syntax_malloc (x, __FILE__, __LINE__)
57 static void *syntax_malloc (size_t x
)
62 p
= mad_alloc (x
, file
, line
);
70 #define syntax_free(x) {if(x){free(x);(x)=0;}}
72 static long compare_word_to_right (WEdit
* edit
, long i
, char *text
, char *whole_left
, char *whole_right
, int line_start
)
78 c
= edit_get_byte (edit
, i
- 1);
83 if (strchr (whole_left
, c
))
85 for (p
= (unsigned char *) text
, q
= p
+ strlen ((char *) p
); (unsigned long) p
< (unsigned long) q
; p
++, i
++) {
90 c
= edit_get_byte (edit
, i
);
93 if (!strchr (whole_right
, c
))
106 c
= edit_get_byte (edit
, i
);
109 if (*p
== *text
&& !p
[1]) /* handle eg '+' and @+@ keywords properly */
112 if (j
&& strchr ((char *) p
+ 1, c
)) /* c exists further down, so it will get matched later */
114 if (c
== '\n' || c
== '\t' || c
== ' ') {
125 if (!strchr (whole_right
, c
)) {
143 c
= edit_get_byte (edit
, i
);
144 for (j
= 0; p
[j
] != '\003'; j
++)
149 j
= c
; /* dummy command */
159 c
= edit_get_byte (edit
, i
);
160 for (; *p
!= '\004'; p
++)
165 for (; *p
!= '\004'; p
++);
168 if (*p
!= edit_get_byte (edit
, i
))
173 if (strchr (whole_right
, edit_get_byte (edit
, i
)))
179 if (*s < '\005' || *s == (unsigned char) c) \
183 static inline char *xx_strchr (const unsigned char *s
, int c
)
186 XXX XXX XXX XXX XXX XXX XXX XXX
;
187 XXX XXX XXX XXX XXX XXX XXX XXX
;
193 static inline struct syntax_rule
apply_rules_going_right (WEdit
* edit
, long i
, struct syntax_rule rule
)
195 struct context_rule
*r
;
196 int contextchanged
= 0, c
;
197 int found_right
= 0, found_left
= 0, keyword_foundleft
= 0, keyword_foundright
= 0;
200 struct syntax_rule _rule
= rule
;
201 if (!(c
= edit_get_byte (edit
, i
)))
203 is_end
= (rule
.end
== (unsigned char) i
);
204 /* check to turn off a keyword */
207 k
= edit
->rules
[_rule
.context
]->keyword
[_rule
.keyword
];
208 if (edit_get_byte (edit
, i
- 1) == '\n')
212 keyword_foundleft
= 1;
215 /* check to turn off a context */
216 if (_rule
.context
&& !_rule
.keyword
) {
218 r
= edit
->rules
[_rule
.context
];
219 if (r
->first_right
== c
&& !(rule
.border
& RULE_ON_RIGHT_BORDER
) && (e
= compare_word_to_right (edit
, i
, r
->right
, r
->whole_word_chars_left
, r
->whole_word_chars_right
, r
->line_start_right
)) > 0) {
222 _rule
.border
= RULE_ON_RIGHT_BORDER
;
223 if (r
->between_delimiters
)
225 } else if (is_end
&& rule
.border
& RULE_ON_RIGHT_BORDER
) {
226 /* always turn off a context at 4 */
229 if (!keyword_foundleft
)
231 } else if (is_end
&& rule
.border
& RULE_ON_LEFT_BORDER
) {
232 /* never turn off a context at 2 */
237 /* check to turn on a keyword */
238 if (!_rule
.keyword
) {
240 p
= (r
= edit
->rules
[_rule
.context
])->keyword_first_chars
;
241 while (*(p
= xx_strchr ((unsigned char *) p
+ 1, c
))) {
245 count
= (unsigned long) p
- (unsigned long) r
->keyword_first_chars
;
246 k
= r
->keyword
[count
];
247 e
= compare_word_to_right (edit
, i
, k
->keyword
, k
->whole_word_chars_left
, k
->whole_word_chars_right
, k
->line_start
);
251 _rule
.keyword
= count
;
252 keyword_foundright
= 1;
257 /* check to turn on a context */
258 if (!_rule
.context
) {
259 if (!found_left
&& is_end
) {
260 if (rule
.border
& RULE_ON_RIGHT_BORDER
) {
265 } else if (rule
.border
& RULE_ON_LEFT_BORDER
) {
266 r
= edit
->rules
[_rule
._context
];
268 if (r
->between_delimiters
) {
270 _rule
.context
= _rule
._context
;
273 if (r
->first_right
== c
&& (e
= compare_word_to_right (edit
, i
, r
->right
, r
->whole_word_chars_left
, r
->whole_word_chars_right
, r
->line_start_right
)) >= end
) {
276 _rule
.border
= RULE_ON_RIGHT_BORDER
;
284 struct context_rule
**rules
= edit
->rules
;
285 for (count
= 1; rules
[count
]; count
++) {
287 if (r
->first_left
== c
) {
289 e
= compare_word_to_right (edit
, i
, r
->left
, r
->whole_word_chars_left
, r
->whole_word_chars_right
, r
->line_start_left
);
290 if (e
>= end
&& (!_rule
.keyword
|| keyword_foundright
)) {
293 _rule
.border
= RULE_ON_LEFT_BORDER
;
294 _rule
._context
= count
;
295 if (!r
->between_delimiters
)
297 _rule
.context
= count
;
304 /* check again to turn on a keyword if the context switched */
305 if (contextchanged
&& !_rule
.keyword
) {
307 p
= (r
= edit
->rules
[_rule
.context
])->keyword_first_chars
;
308 while (*(p
= xx_strchr ((unsigned char *) p
+ 1, c
))) {
312 count
= (unsigned long) p
- (unsigned long) r
->keyword_first_chars
;
313 k
= r
->keyword
[count
];
314 e
= compare_word_to_right (edit
, i
, k
->keyword
, k
->whole_word_chars_left
, k
->whole_word_chars_right
, k
->line_start
);
317 _rule
.keyword
= count
;
325 static struct syntax_rule
edit_get_rule (WEdit
* edit
, long byte_index
)
328 if (byte_index
> edit
->last_get_rule
) {
329 for (i
= edit
->last_get_rule
+ 1; i
<= byte_index
; i
++) {
330 edit
->rule
= apply_rules_going_right (edit
, i
, edit
->rule
);
331 if (i
> (edit
->syntax_marker
? edit
->syntax_marker
->offset
+ SYNTAX_MARKER_DENSITY
: SYNTAX_MARKER_DENSITY
)) {
332 struct _syntax_marker
*s
;
333 s
= edit
->syntax_marker
;
334 edit
->syntax_marker
= syntax_malloc (sizeof (struct _syntax_marker
));
335 edit
->syntax_marker
->next
= s
;
336 edit
->syntax_marker
->offset
= i
;
337 edit
->syntax_marker
->rule
= edit
->rule
;
340 } else if (byte_index
< edit
->last_get_rule
) {
341 struct _syntax_marker
*s
;
343 if (!edit
->syntax_marker
) {
344 memset (&edit
->rule
, 0, sizeof (edit
->rule
));
345 for (i
= -1; i
<= byte_index
; i
++)
346 edit
->rule
= apply_rules_going_right (edit
, i
, edit
->rule
);
349 if (byte_index
>= edit
->syntax_marker
->offset
) {
350 edit
->rule
= edit
->syntax_marker
->rule
;
351 for (i
= edit
->syntax_marker
->offset
+ 1; i
<= byte_index
; i
++)
352 edit
->rule
= apply_rules_going_right (edit
, i
, edit
->rule
);
355 s
= edit
->syntax_marker
->next
;
356 syntax_free (edit
->syntax_marker
);
357 edit
->syntax_marker
= s
;
360 edit
->last_get_rule
= byte_index
;
364 static void translate_rule_to_color (WEdit
* edit
, struct syntax_rule rule
, int *fg
, int *bg
)
367 k
= edit
->rules
[rule
.context
]->keyword
[rule
.keyword
];
372 extern int use_colors
;
374 void edit_get_syntax_color (WEdit
* edit
, long byte_index
, int *fg
, int *bg
)
376 if (edit
->rules
&& byte_index
< edit
->last_byte
&&
377 option_syntax_highlighting
&& use_colors
) {
378 translate_rule_to_color (edit
, edit_get_rule (edit
, byte_index
), fg
, bg
);
380 *fg
= EDITOR_NORMAL_COLOR
;
386 Returns 0 on error/eof or a count of the number of bytes read
387 including the newline. Result must be free'd.
390 static int mad_read_one_line (char **line
, FILE * f
, char *file
, int line_
)
391 #define read_one_line(a,b) mad_read_one_line(a,b,__FILE__,__LINE__)
393 static int read_one_line (char **line
, FILE * f
)
397 int len
= 256, c
, r
= 0, i
= 0;
399 p
= mad_syntax_malloc (len
, file
, line_
);
401 p
= syntax_malloc (len
);
410 } else if (c
== '\n') {
411 r
= i
+ 1; /* extra 1 for the newline just read */
416 q
= syntax_malloc (len
* 2);
430 static char *strdup_convert (char *s
)
433 p
= r
= (char *) strdup (s
);
491 #define whiteness(x) ((x) == '\t' || (x) == '\n' || (x) == ' ')
493 static void get_args (char *l
, char **args
, int *argc
)
499 for (p
= l
+ 1; *p
&& whiteness (*p
); p
++);
502 for (l
= p
+ 1; *l
&& !whiteness (*l
); l
++);
504 *args
= strdup_convert (p
);
511 static void free_args (char **args
)
520 #define check_a {if(!*a){result=line;break;}}
521 #define check_not_a {if(*a){result=line;break;}}
523 int try_alloc_color_pair (char *fg
, char *bg
);
525 int this_try_alloc_color_pair (char *fg
, char *bg
)
527 char f
[80], b
[80], *p
;
548 return try_alloc_color_pair (fg
, bg
);
551 static char *error_file_name
= 0;
553 extern char *mc_home
;
555 static FILE *open_include_file (char *filename
)
558 char p
[MAX_PATH_LEN
];
559 syntax_free (error_file_name
);
560 error_file_name
= (char *) strdup (filename
);
561 if (*filename
== '/')
562 return fopen (filename
, "r");
563 strcpy (p
, home_dir
);
564 strcat (p
, EDIT_DIR
"/");
565 strcat (p
, filename
);
566 syntax_free (error_file_name
);
567 error_file_name
= (char *) strdup (p
);
572 strcat (p
, "/syntax/");
573 strcat (p
, filename
);
574 syntax_free (error_file_name
);
575 error_file_name
= (char *) strdup (p
);
576 return fopen (p
, "r");
579 /* returns line number on error */
580 static int edit_read_syntax_rules (WEdit
* edit
, FILE * f
)
584 char last_fg
[32] = "", last_bg
[32] = "";
585 char whole_right
[512];
586 char whole_left
[512];
587 char *args
[1024], *l
= 0;
588 int save_line
= 0, line
= 0;
589 struct context_rule
**r
, *c
= 0;
590 int num_words
= -1, num_contexts
= -1;
591 int argc
, result
= 0;
596 strcpy (whole_left
, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
597 strcpy (whole_right
, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
599 r
= edit
->rules
= syntax_malloc (MAX_CONTEXTS
* sizeof (struct context_rule
*));
605 if (!read_one_line (&l
, f
)) {
610 line
= save_line
+ 1;
611 syntax_free (error_file_name
);
614 if (!read_one_line (&l
, f
))
620 get_args (l
, args
, &argc
);
624 } else if (!strcmp (args
[0], "include")) {
625 if (g
|| argc
!= 2) {
630 f
= open_include_file (args
[1]);
632 syntax_free (error_file_name
);
638 } else if (!strcmp (args
[0], "wholechars")) {
640 if (!strcmp (*a
, "left")) {
642 strcpy (whole_left
, *a
);
643 } else if (!strcmp (*a
, "right")) {
645 strcpy (whole_right
, *a
);
647 strcpy (whole_left
, *a
);
648 strcpy (whole_right
, *a
);
652 } else if (!strcmp (args
[0], "context")) {
654 if (num_contexts
== -1) {
655 if (strcmp (*a
, "default")) { /* first context is the default */
660 c
= r
[0] = syntax_malloc (sizeof (struct context_rule
));
661 c
->left
= (char *) strdup (" ");
662 c
->right
= (char *) strdup (" ");
665 c
= r
[num_contexts
] = syntax_malloc (sizeof (struct context_rule
));
666 if (!strcmp (*a
, "exclusive")) {
668 c
->between_delimiters
= 1;
671 if (!strcmp (*a
, "whole")) {
673 c
->whole_word_chars_left
= (char *) strdup (whole_left
);
674 c
->whole_word_chars_right
= (char *) strdup (whole_right
);
675 } else if (!strcmp (*a
, "wholeleft")) {
677 c
->whole_word_chars_left
= (char *) strdup (whole_left
);
678 } else if (!strcmp (*a
, "wholeright")) {
680 c
->whole_word_chars_right
= (char *) strdup (whole_right
);
683 if (!strcmp (*a
, "linestart")) {
685 c
->line_start_left
= 1;
688 c
->left
= (char *) strdup (*a
++);
690 if (!strcmp (*a
, "linestart")) {
692 c
->line_start_right
= 1;
695 c
->right
= (char *) strdup (*a
++);
696 c
->first_left
= *c
->left
;
697 c
->first_right
= *c
->right
;
698 c
->single_char
= (strlen (c
->right
) == 1);
700 c
->keyword
= syntax_malloc (MAX_WORDS_PER_CONTEXT
* sizeof (struct key_word
*));
702 c
->max_words
= MAX_WORDS_PER_CONTEXT
;
705 c
->keyword
[0] = syntax_malloc (sizeof (struct key_word
));
712 strcpy (last_fg
, fg
? fg
: "");
713 strcpy (last_bg
, bg
? bg
: "");
714 c
->keyword
[0]->fg
= this_try_alloc_color_pair (fg
, bg
);
715 c
->keyword
[0]->keyword
= (char *) strdup (" ");
718 } else if (!strcmp (args
[0], "spellcheck")) {
724 } else if (!strcmp (args
[0], "keyword")) {
729 k
= r
[num_contexts
- 1]->keyword
[num_words
] = syntax_malloc (sizeof (struct key_word
));
730 if (!strcmp (*a
, "whole")) {
732 k
->whole_word_chars_left
= (char *) strdup (whole_left
);
733 k
->whole_word_chars_right
= (char *) strdup (whole_right
);
734 } else if (!strcmp (*a
, "wholeleft")) {
736 k
->whole_word_chars_left
= (char *) strdup (whole_left
);
737 } else if (!strcmp (*a
, "wholeright")) {
739 k
->whole_word_chars_right
= (char *) strdup (whole_right
);
742 if (!strcmp (*a
, "linestart")) {
747 if (!strcmp (*a
, "whole")) {
751 k
->keyword
= (char *) strdup (*a
++);
752 k
->first
= *k
->keyword
;
763 k
->fg
= this_try_alloc_color_pair (fg
, bg
);
766 } else if (!strncmp (args
[0], "#", 1)) {
767 /* do nothing for comment */
768 } else if (!strcmp (args
[0], "file")) {
770 } else { /* anything else is an error */
781 syntax_free (edit
->rules
);
786 if (num_contexts
== -1) {
792 char first_chars
[MAX_WORDS_PER_CONTEXT
+ 2], *p
;
793 for (i
= 0; edit
->rules
[i
]; i
++) {
797 for (j
= 1; c
->keyword
[j
]; j
++)
798 *p
++ = c
->keyword
[j
]->first
;
800 c
->keyword_first_chars
= syntax_malloc (strlen (first_chars
) + 2);
801 strcpy (c
->keyword_first_chars
, first_chars
);
808 int edit_check_spelling (WEdit
* edit
)
813 void (*syntax_change_callback
) (CWidget
*) = 0;
815 void edit_set_syntax_change_callback (void (*callback
) (CWidget
*))
817 syntax_change_callback
= callback
;
820 void edit_free_syntax_rules (WEdit
* edit
)
827 edit_get_rule (edit
, -1);
828 syntax_free (edit
->syntax_type
);
829 edit
->syntax_type
= 0;
830 if (syntax_change_callback
)
831 (*syntax_change_callback
) (&edit
->widget
);
832 for (i
= 0; edit
->rules
[i
]; i
++) {
833 if (edit
->rules
[i
]->keyword
) {
834 for (j
= 0; edit
->rules
[i
]->keyword
[j
]; j
++) {
835 syntax_free (edit
->rules
[i
]->keyword
[j
]->keyword
);
836 syntax_free (edit
->rules
[i
]->keyword
[j
]->whole_word_chars_left
);
837 syntax_free (edit
->rules
[i
]->keyword
[j
]->whole_word_chars_right
);
838 syntax_free (edit
->rules
[i
]->keyword
[j
]);
841 syntax_free (edit
->rules
[i
]->left
);
842 syntax_free (edit
->rules
[i
]->right
);
843 syntax_free (edit
->rules
[i
]->whole_word_chars_left
);
844 syntax_free (edit
->rules
[i
]->whole_word_chars_right
);
845 syntax_free (edit
->rules
[i
]->keyword
);
846 syntax_free (edit
->rules
[i
]->keyword_first_chars
);
847 syntax_free (edit
->rules
[i
]);
849 while (edit
->syntax_marker
) {
850 struct _syntax_marker
*s
= edit
->syntax_marker
->next
;
851 syntax_free (edit
->syntax_marker
);
852 edit
->syntax_marker
= s
;
854 syntax_free (edit
->rules
);
857 /* returns -1 on file error, line number on error in file syntax */
858 static int edit_read_syntax_file (WEdit
* edit
, char **names
, char *syntax_file
, char *editor_file
, char *first_line
, char *type
)
862 regmatch_t pmatch
[1];
863 char *args
[1024], *l
= 0;
870 lib_file
= concat_dir_and_file (mc_home
, "syntax/Syntax");
871 check_for_default (lib_file
, syntax_file
);
874 f
= fopen (syntax_file
, "r");
881 if (!read_one_line (&l
, f
))
883 get_args (l
, args
, &argc
);
886 /* looking for `file ...' lines only */
887 if (strcmp (args
[0], "file")) {
891 /* must have two args or report error */
892 if (!args
[1] || !args
[2]) {
897 /* 1: just collecting a list of names of rule sets */
898 names
[count
++] = (char *) strdup (args
[2]);
901 /* 2: rule set was explicitly specified by the caller */
902 if (!strcmp (type
, args
[2]))
904 } else if (editor_file
&& edit
) {
905 /* 3: auto-detect rule set from regular expressions */
907 if (regcomp (&r
, args
[1], REG_EXTENDED
)) {
911 /* does filename match arg 1 ? */
912 q
= !regexec (&r
, editor_file
, 1, pmatch
, 0);
915 if (regcomp (&r
, args
[3], REG_EXTENDED
)) {
919 /* does first line match arg 3 ? */
920 q
= !regexec (&r
, first_line
, 1, pmatch
, 0);
926 line_error
= edit_read_syntax_rules (edit
, f
);
928 if (!error_file_name
) /* an included file */
929 result
= line
+ line_error
;
933 syntax_free (edit
->syntax_type
);
934 edit
->syntax_type
= (char *) strdup (args
[2]);
935 /* if there are no rules then turn off syntax highlighting for speed */
937 if (!edit
->rules
[0]->keyword
[1] && !edit
->rules
[0]->spelling
) {
938 edit_free_syntax_rules (edit
);
941 /* notify the callback of a change in rule set */
942 if (syntax_change_callback
)
943 (*syntax_change_callback
) (&edit
->widget
);
956 static char *get_first_editor_line (WEdit
* edit
)
963 for (i
= 0; i
< 255; i
++) {
964 s
[i
] = edit_get_byte (edit
, i
);
974 /* loads rules into edit struct. one of edit or names must be zero. if
975 edit is zero, a list of types will be stored into name. type may be zero
976 in which case the type will be selected according to the filename. */
977 void edit_load_syntax (WEdit
* edit
, char **names
, char *type
)
982 edit_free_syntax_rules (edit
);
984 if (!option_syntax_highlighting
)
990 if (!*edit
->filename
&& !type
)
993 f
= catstrs (home_dir
, SYNTAX_FILE
, 0);
994 r
= edit_read_syntax_file (edit
, names
, f
, edit
? edit
->filename
: 0, get_first_editor_line (edit
), type
);
996 edit_free_syntax_rules (edit
);
997 edit_error_dialog (_ (" Load syntax file "), _ (" File access error "));
1002 edit_free_syntax_rules (edit
);
1003 sprintf (s
, _ (" Error in file %s on line %d "), error_file_name
? error_file_name
: f
, r
);
1004 edit_error_dialog (_ (" Load syntax file "), s
);
1005 syntax_free (error_file_name
);
1012 int option_syntax_highlighting
= 0;
1014 void edit_load_syntax (WEdit
* edit
, char **names
, char *type
)
1019 void edit_free_syntax_rules (WEdit
* edit
)
1024 void edit_get_syntax_color (WEdit
* edit
, long byte_index
, int *fg
, int *bg
)
1029 int edit_check_spelling (WEdit
* edit
)
1034 #endif /* HAVE_SYNTAXH */