2 Editor syntax highlighting.
4 Copyright (C) 1996-2016
5 Free Software Foundation, Inc.
9 Egmont Koblinger <egmont@gmail.com>, 2010
10 Slava Zanko <slavazanko@gmail.com>, 2013
11 Andrew Borodin <aborodin@vmail.ru>, 2013, 2014
13 This file is part of the Midnight Commander.
15 The Midnight Commander is free software: you can redistribute it
16 and/or modify it under the terms of the GNU General Public License as
17 published by the Free Software Foundation, either version 3 of the License,
18 or (at your option) any later version.
20 The Midnight Commander is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program. If not, see <http://www.gnu.org/licenses/>.
30 * \brief Source: editor syntax highlighting
33 * \author Mikhail Pobolovets
36 * Mispelled words are flushed from the syntax highlighting rules
37 * when they have been around longer than
38 * TRANSIENT_WORD_TIME_OUT seconds. At a cursor rate of 30
39 * chars per second and say 3 chars + a space per word, we can
40 * accumulate 450 words absolute max with a value of 60. This is
41 * below this limit of 1024 words in a context.
48 #include <sys/types.h>
56 #include "lib/global.h"
57 #include "lib/search.h" /* search engine */
59 #include "lib/fileloc.h" /* EDIT_DIR, EDIT_SYNTAX_FILE */
60 #include "lib/strutil.h" /* utf string functions */
62 #include "lib/widget.h" /* message() */
64 #include "edit-impl.h"
65 #include "editwidget.h"
67 /*** global variables ****************************************************************************/
69 gboolean option_syntax_highlighting
= TRUE
;
70 gboolean option_auto_syntax
= TRUE
;
72 /*** file scope macro definitions ****************************************************************/
75 #define SYNTAX_MARKER_DENSITY 512
77 #define RULE_ON_LEFT_BORDER 1
78 #define RULE_ON_RIGHT_BORDER 2
80 #define SYNTAX_TOKEN_STAR '\001'
81 #define SYNTAX_TOKEN_PLUS '\002'
82 #define SYNTAX_TOKEN_BRACKET '\003'
83 #define SYNTAX_TOKEN_BRACE '\004'
85 #define whiteness(x) ((x) == '\t' || (x) == '\n' || (x) == ' ')
88 #define break_a {result=line;break;}
89 #define check_a {if(!*a){result=line;break;}}
90 #define check_not_a {if(*a){result=line;break;}}
92 #define SYNTAX_KEYWORD(x) ((syntax_keyword_t *) (x))
93 #define CONTEXT_RULE(x) ((context_rule_t *) (x))
95 /*** file scope type declarations ****************************************************************/
100 char *whole_word_chars_left
;
101 char *whole_word_chars_right
;
109 unsigned char first_left
;
111 unsigned char first_right
;
112 char line_start_left
;
113 char line_start_right
;
114 int between_delimiters
;
115 char *whole_word_chars_left
;
116 char *whole_word_chars_right
;
117 char *keyword_first_chars
;
119 /* first word is word[1] */
126 edit_syntax_rule_t rule
;
129 /*** file scope variables ************************************************************************/
131 static char *error_file_name
= NULL
;
133 /* --------------------------------------------------------------------------------------------- */
134 /*** file scope functions ************************************************************************/
135 /* --------------------------------------------------------------------------------------------- */
138 syntax_keyword_free (gpointer keyword
)
140 syntax_keyword_t
*k
= SYNTAX_KEYWORD (keyword
);
143 g_free (k
->whole_word_chars_left
);
144 g_free (k
->whole_word_chars_right
);
148 /* --------------------------------------------------------------------------------------------- */
151 context_rule_free (gpointer rule
)
153 context_rule_t
*r
= CONTEXT_RULE (rule
);
157 g_free (r
->whole_word_chars_left
);
158 g_free (r
->whole_word_chars_right
);
159 g_free (r
->keyword_first_chars
);
161 if (r
->keyword
!= NULL
)
163 g_ptr_array_foreach (r
->keyword
, (GFunc
) syntax_keyword_free
, NULL
);
164 g_ptr_array_free (r
->keyword
, TRUE
);
170 /* --------------------------------------------------------------------------------------------- */
173 mc_defines_destroy (gpointer key
, gpointer value
, gpointer data
)
178 g_strfreev ((char **) value
);
183 /* --------------------------------------------------------------------------------------------- */
184 /** Completely destroys the defines tree */
187 destroy_defines (GTree
** defines
)
189 g_tree_foreach (*defines
, mc_defines_destroy
, NULL
);
190 g_tree_destroy (*defines
);
194 /* --------------------------------------------------------------------------------------------- */
196 /** Wrapper for case insensitive mode */
198 xx_tolower (const WEdit
* edit
, int c
)
200 return edit
->is_case_insensitive
? tolower (c
) : c
;
203 /* --------------------------------------------------------------------------------------------- */
206 subst_defines (GTree
* defines
, char **argv
, char **argv_end
)
211 while (*argv
!= NULL
&& argv
< argv_end
)
213 t
= g_tree_lookup (defines
, *argv
);
218 /* Count argv array members */
220 for (p
= &argv
[1]; *p
!= NULL
; p
++)
223 /* Count members of definition array */
224 for (p
= t
; *p
!= NULL
; p
++)
226 p
= &argv
[count
+ argc
];
228 /* Buffer overflow or infinitive loop in define */
232 /* Move rest of argv after definition members */
234 *p
-- = argv
[argc
-- + 1];
236 /* Copy definition members to argv */
237 for (p
= argv
; *t
!= NULL
; *p
++ = *t
++)
244 /* --------------------------------------------------------------------------------------------- */
247 compare_word_to_right (const WEdit
* edit
, off_t i
, const char *text
,
248 const char *whole_left
, const char *whole_right
, long line_start
)
250 const unsigned char *p
, *q
;
256 c
= xx_tolower (edit
, edit_buffer_get_byte (&edit
->buffer
, i
- 1));
257 if ((line_start
!= 0 && c
!= '\n') || (whole_left
!= NULL
&& strchr (whole_left
, c
) != NULL
))
260 for (p
= (const unsigned char *) text
, q
= p
+ strlen ((const char *) p
); p
< q
; p
++, i
++)
264 case SYNTAX_TOKEN_STAR
:
269 c
= xx_tolower (edit
, edit_buffer_get_byte (&edit
->buffer
, i
));
270 if (*p
== '\0' && whole_right
!= NULL
&& strchr (whole_right
, c
) == NULL
)
279 case SYNTAX_TOKEN_PLUS
:
285 c
= xx_tolower (edit
, edit_buffer_get_byte (&edit
->buffer
, i
));
289 if (*p
== *text
&& p
[1] == '\0') /* handle eg '+' and @+@ keywords properly */
292 if (j
!= 0 && strchr ((const char *) p
+ 1, c
) != NULL
) /* c exists further down, so it will get matched later */
294 if (c
== '\n' || c
== '\t' || c
== ' ' ||
295 (whole_right
!= NULL
&& strchr (whole_right
, c
) == NULL
))
310 case SYNTAX_TOKEN_BRACKET
:
317 c
= xx_tolower (edit
, edit_buffer_get_byte (&edit
->buffer
, i
));
318 for (j
= 0; p
[j
] != SYNTAX_TOKEN_BRACKET
&& p
[j
]; j
++)
326 while (*p
!= SYNTAX_TOKEN_BRACKET
&& p
<= q
)
333 case SYNTAX_TOKEN_BRACE
:
336 c
= xx_tolower (edit
, edit_buffer_get_byte (&edit
->buffer
, i
));
337 for (; *p
!= SYNTAX_TOKEN_BRACE
&& *p
; p
++)
342 while (*p
!= SYNTAX_TOKEN_BRACE
&& p
< q
)
346 if (*p
!= xx_tolower (edit
, edit_buffer_get_byte (&edit
->buffer
, i
)))
350 return (whole_right
!= NULL
&&
352 xx_tolower (edit
, edit_buffer_get_byte (&edit
->buffer
, i
))) != NULL
) ? -1 : i
;
355 /* --------------------------------------------------------------------------------------------- */
358 xx_strchr (const WEdit
* edit
, const unsigned char *s
, int char_byte
)
360 while (*s
>= '\005' && xx_tolower (edit
, *s
) != char_byte
)
363 return (const char *) s
;
366 /* --------------------------------------------------------------------------------------------- */
369 apply_rules_going_right (WEdit
* edit
, off_t i
)
373 gboolean contextchanged
= FALSE
;
374 gboolean found_left
= FALSE
, found_right
= FALSE
;
375 gboolean keyword_foundleft
= FALSE
, keyword_foundright
= FALSE
;
378 edit_syntax_rule_t _rule
= edit
->rule
;
380 c
= xx_tolower (edit
, edit_buffer_get_byte (&edit
->buffer
, i
));
384 is_end
= (edit
->rule
.end
== i
);
386 /* check to turn off a keyword */
387 if (_rule
.keyword
!= 0)
389 if (edit_buffer_get_byte (&edit
->buffer
, i
- 1) == '\n')
394 keyword_foundleft
= TRUE
;
398 /* check to turn off a context */
399 if (_rule
.context
!= 0 && _rule
.keyword
== 0)
403 r
= CONTEXT_RULE (g_ptr_array_index (edit
->rules
, _rule
.context
));
404 if (r
->first_right
== c
&& (edit
->rule
.border
& RULE_ON_RIGHT_BORDER
) == 0
406 compare_word_to_right (edit
, i
, r
->right
, r
->whole_word_chars_left
,
407 r
->whole_word_chars_right
, r
->line_start_right
)) > 0)
411 _rule
.border
= RULE_ON_RIGHT_BORDER
;
412 if (r
->between_delimiters
)
415 else if (is_end
&& (edit
->rule
.border
& RULE_ON_RIGHT_BORDER
) != 0)
417 /* always turn off a context at 4 */
420 if (!keyword_foundleft
)
423 else if (is_end
&& (edit
->rule
.border
& RULE_ON_LEFT_BORDER
) != 0)
425 /* never turn off a context at 2 */
431 /* check to turn on a keyword */
432 if (_rule
.keyword
== 0)
436 r
= CONTEXT_RULE (g_ptr_array_index (edit
->rules
, _rule
.context
));
437 p
= r
->keyword_first_chars
;
440 while (*(p
= xx_strchr (edit
, (const unsigned char *) p
+ 1, c
)) != '\0')
446 count
= p
- r
->keyword_first_chars
;
447 k
= SYNTAX_KEYWORD (g_ptr_array_index (r
->keyword
, count
));
448 e
= compare_word_to_right (edit
, i
, k
->keyword
, k
->whole_word_chars_left
,
449 k
->whole_word_chars_right
, k
->line_start
);
454 _rule
.keyword
= count
;
455 keyword_foundright
= TRUE
;
461 /* check to turn on a context */
462 if (_rule
.context
== 0)
464 if (!found_left
&& is_end
)
466 if ((edit
->rule
.border
& RULE_ON_RIGHT_BORDER
) != 0)
470 contextchanged
= TRUE
;
474 else if ((edit
->rule
.border
& RULE_ON_LEFT_BORDER
) != 0)
476 r
= CONTEXT_RULE (g_ptr_array_index (edit
->rules
, _rule
._context
));
478 if (r
->between_delimiters
)
480 _rule
.context
= _rule
._context
;
481 contextchanged
= TRUE
;
484 if (r
->first_right
== c
)
488 e
= compare_word_to_right (edit
, i
, r
->right
, r
->whole_word_chars_left
,
489 r
->whole_word_chars_right
, r
->line_start_right
);
494 _rule
.border
= RULE_ON_RIGHT_BORDER
;
506 for (count
= 1; count
< edit
->rules
->len
; count
++)
508 r
= CONTEXT_RULE (g_ptr_array_index (edit
->rules
, count
));
509 if (r
->first_left
== c
)
513 e
= compare_word_to_right (edit
, i
, r
->left
, r
->whole_word_chars_left
,
514 r
->whole_word_chars_right
, r
->line_start_left
);
515 if (e
>= end
&& (_rule
.keyword
== 0 || keyword_foundright
))
519 _rule
.border
= RULE_ON_LEFT_BORDER
;
520 _rule
._context
= count
;
521 if (!r
->between_delimiters
&& _rule
.keyword
== 0)
523 _rule
.context
= count
;
524 contextchanged
= TRUE
;
533 /* check again to turn on a keyword if the context switched */
534 if (contextchanged
&& _rule
.keyword
== 0)
538 r
= CONTEXT_RULE (g_ptr_array_index (edit
->rules
, _rule
.context
));
539 p
= r
->keyword_first_chars
;
541 while (*(p
= xx_strchr (edit
, (const unsigned char *) p
+ 1, c
)) != '\0')
547 count
= p
- r
->keyword_first_chars
;
548 k
= SYNTAX_KEYWORD (g_ptr_array_index (r
->keyword
, count
));
549 e
= compare_word_to_right (edit
, i
, k
->keyword
, k
->whole_word_chars_left
,
550 k
->whole_word_chars_right
, k
->line_start
);
554 _rule
.keyword
= count
;
563 /* --------------------------------------------------------------------------------------------- */
566 edit_get_rule (WEdit
* edit
, off_t byte_index
)
570 if (byte_index
> edit
->last_get_rule
)
572 for (i
= edit
->last_get_rule
+ 1; i
<= byte_index
; i
++)
574 off_t d
= SYNTAX_MARKER_DENSITY
;
576 apply_rules_going_right (edit
, i
);
578 if (edit
->syntax_marker
!= NULL
)
579 d
+= ((syntax_marker_t
*) edit
->syntax_marker
->data
)->offset
;
585 s
= g_new (syntax_marker_t
, 1);
587 s
->rule
= edit
->rule
;
588 edit
->syntax_marker
= g_slist_prepend (edit
->syntax_marker
, s
);
592 else if (byte_index
< edit
->last_get_rule
)
598 if (edit
->syntax_marker
== NULL
)
600 memset (&edit
->rule
, 0, sizeof (edit
->rule
));
601 for (i
= -1; i
<= byte_index
; i
++)
602 apply_rules_going_right (edit
, i
);
606 s
= (syntax_marker_t
*) edit
->syntax_marker
->data
;
608 if (byte_index
>= s
->offset
)
610 edit
->rule
= s
->rule
;
611 for (i
= s
->offset
+ 1; i
<= byte_index
; i
++)
612 apply_rules_going_right (edit
, i
);
617 edit
->syntax_marker
= g_slist_delete_link (edit
->syntax_marker
, edit
->syntax_marker
);
620 edit
->last_get_rule
= byte_index
;
623 /* --------------------------------------------------------------------------------------------- */
626 translate_rule_to_color (const WEdit
* edit
, const edit_syntax_rule_t
* rule
)
631 r
= CONTEXT_RULE (g_ptr_array_index (edit
->rules
, rule
->context
));
632 k
= SYNTAX_KEYWORD (g_ptr_array_index (r
->keyword
, rule
->keyword
));
637 /* --------------------------------------------------------------------------------------------- */
639 Returns 0 on error/eof or a count of the number of bytes read
640 including the newline. Result must be free'd.
641 In case of an error, *line will not be modified.
645 read_one_line (char **line
, FILE * f
)
650 /* not reallocate string too often */
651 p
= g_string_sized_new (64);
670 /* handle all of \r\n, \r, \n correctly. */
683 g_string_append_c (p
, c
);
686 *line
= g_string_free (p
, FALSE
);
688 g_string_free (p
, TRUE
);
693 /* --------------------------------------------------------------------------------------------- */
733 *p
= SYNTAX_TOKEN_BRACKET
;
737 *p
= SYNTAX_TOKEN_BRACE
;
748 *p
= SYNTAX_TOKEN_STAR
;
751 *p
= SYNTAX_TOKEN_PLUS
;
764 /* --------------------------------------------------------------------------------------------- */
767 get_args (char *l
, char **args
, int args_size
)
771 while (argc
< args_size
)
774 while (*p
!= '\0' && whiteness (*p
))
778 for (l
= p
+ 1; *l
!= '\0' && !whiteness (*l
); l
++)
782 args
[argc
++] = convert (p
);
784 args
[argc
] = (char *) NULL
;
788 /* --------------------------------------------------------------------------------------------- */
791 this_try_alloc_color_pair (const char *fg
, const char *bg
, const char *attrs
)
793 char f
[80], b
[80], a
[80], *p
;
795 if (bg
!= NULL
&& *bg
== '\0')
797 if (fg
!= NULL
&& *fg
== '\0')
799 if (attrs
!= NULL
&& *attrs
== '\0')
802 if ((fg
== NULL
) && (bg
== NULL
))
803 return EDITOR_NORMAL_COLOR
;
807 g_strlcpy (f
, fg
, sizeof (f
));
815 g_strlcpy (b
, bg
, sizeof (b
));
821 if ((fg
== NULL
) || (bg
== NULL
))
823 /* get colors from skin */
826 editnormal
= mc_skin_get ("editor", "_default_", "default;default");
830 g_strlcpy (f
, editnormal
, sizeof (f
));
835 g_strlcpy (f
, "default", sizeof (f
));
840 p
= strchr (editnormal
, ';');
841 if ((p
!= NULL
) && (*(++p
) != '\0'))
842 g_strlcpy (b
, p
, sizeof (b
));
844 g_strlcpy (b
, "default", sizeof (b
));
853 g_strlcpy (a
, attrs
, sizeof (a
));
857 /* get_args() mangles the + signs, unmangle 'em */
859 while ((p
= strchr (p
, SYNTAX_TOKEN_PLUS
)) != NULL
)
863 return tty_try_alloc_color_pair (fg
, bg
, attrs
);
866 /* --------------------------------------------------------------------------------------------- */
869 open_include_file (const char *filename
)
873 MC_PTR_FREE (error_file_name
);
874 error_file_name
= g_strdup (filename
);
875 if (g_path_is_absolute (filename
))
876 return fopen (filename
, "r");
878 g_free (error_file_name
);
880 g_build_filename (mc_config_get_data_path (), EDIT_DIR
, filename
, (char *) NULL
);
881 f
= fopen (error_file_name
, "r");
885 g_free (error_file_name
);
886 error_file_name
= g_build_filename (mc_global
.sysconfig_dir
, "syntax", filename
, (char *) NULL
);
887 f
= fopen (error_file_name
, "r");
891 g_free (error_file_name
);
893 g_build_filename (mc_global
.share_data_dir
, "syntax", filename
, (char *) NULL
);
895 return fopen (error_file_name
, "r");
898 /* --------------------------------------------------------------------------------------------- */
901 xx_lowerize_line (WEdit
* edit
, char *line
, size_t len
)
903 if (edit
->is_case_insensitive
)
906 for (i
= 0; i
< len
; ++i
)
907 line
[i
] = tolower (line
[i
]);
911 /* --------------------------------------------------------------------------------------------- */
912 /** returns line number on error */
915 edit_read_syntax_rules (WEdit
* edit
, FILE * f
, char **args
, int args_size
)
918 char *fg
, *bg
, *attrs
;
919 char last_fg
[32] = "", last_bg
[32] = "", last_attrs
[64] = "";
920 char whole_right
[512];
921 char whole_left
[512];
923 int save_line
= 0, line
= 0;
924 context_rule_t
*c
= NULL
;
925 gboolean no_words
= TRUE
;
929 edit
->is_case_insensitive
= FALSE
;
931 strcpy (whole_left
, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
932 strcpy (whole_right
, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
934 edit
->rules
= g_ptr_array_new ();
937 edit
->defines
= g_tree_new ((GCompareFunc
) strcmp
);
948 len
= read_one_line (&l
, f
);
950 xx_lowerize_line (edit
, l
, len
);
959 line
= save_line
+ 1;
960 MC_PTR_FREE (error_file_name
);
962 len
= read_one_line (&l
, f
);
965 xx_lowerize_line (edit
, l
, len
);
968 argc
= get_args (l
, args
, args_size
);
974 else if (strcmp (args
[0], "include") == 0)
976 if (g
!= NULL
|| argc
!= 2)
982 f
= open_include_file (args
[1]);
985 MC_PTR_FREE (error_file_name
);
992 else if (strcmp (args
[0], "caseinsensitive") == 0)
994 edit
->is_case_insensitive
= TRUE
;
996 else if (strcmp (args
[0], "wholechars") == 0)
999 if (strcmp (*a
, "left") == 0)
1002 g_strlcpy (whole_left
, *a
, sizeof (whole_left
));
1004 else if (strcmp (*a
, "right") == 0)
1007 g_strlcpy (whole_right
, *a
, sizeof (whole_right
));
1011 g_strlcpy (whole_left
, *a
, sizeof (whole_left
));
1012 g_strlcpy (whole_right
, *a
, sizeof (whole_right
));
1017 else if (strcmp (args
[0], "context") == 0)
1019 syntax_keyword_t
*k
;
1022 if (edit
->rules
->len
== 0)
1024 /* first context is the default */
1025 if (strcmp (*a
, "default") != 0)
1029 c
= g_new0 (context_rule_t
, 1);
1030 g_ptr_array_add (edit
->rules
, c
);
1031 c
->left
= g_strdup (" ");
1032 c
->right
= g_strdup (" ");
1036 /* Start new context. */
1037 c
= g_new0 (context_rule_t
, 1);
1038 g_ptr_array_add (edit
->rules
, c
);
1039 if (strcmp (*a
, "exclusive") == 0)
1042 c
->between_delimiters
= 1;
1045 if (strcmp (*a
, "whole") == 0)
1048 c
->whole_word_chars_left
= g_strdup (whole_left
);
1049 c
->whole_word_chars_right
= g_strdup (whole_right
);
1051 else if (strcmp (*a
, "wholeleft") == 0)
1054 c
->whole_word_chars_left
= g_strdup (whole_left
);
1056 else if (strcmp (*a
, "wholeright") == 0)
1059 c
->whole_word_chars_right
= g_strdup (whole_right
);
1062 if (strcmp (*a
, "linestart") == 0)
1065 c
->line_start_left
= 1;
1068 c
->left
= g_strdup (*a
++);
1070 if (strcmp (*a
, "linestart") == 0)
1073 c
->line_start_right
= 1;
1076 c
->right
= g_strdup (*a
++);
1077 c
->first_left
= *c
->left
;
1078 c
->first_right
= *c
->right
;
1080 c
->keyword
= g_ptr_array_new ();
1081 k
= g_new0 (syntax_keyword_t
, 1);
1082 g_ptr_array_add (c
->keyword
, k
);
1084 subst_defines (edit
->defines
, a
, &args
[1024]);
1094 g_strlcpy (last_fg
, fg
!= NULL
? fg
: "", sizeof (last_fg
));
1095 g_strlcpy (last_bg
, bg
!= NULL
? bg
: "", sizeof (last_bg
));
1096 g_strlcpy (last_attrs
, attrs
!= NULL
? attrs
: "", sizeof (last_attrs
));
1097 k
->color
= this_try_alloc_color_pair (fg
, bg
, attrs
);
1098 k
->keyword
= g_strdup (" ");
1101 else if (strcmp (args
[0], "spellcheck") == 0)
1110 else if (strcmp (args
[0], "keyword") == 0)
1112 context_rule_t
*last_rule
;
1113 syntax_keyword_t
*k
;
1118 last_rule
= CONTEXT_RULE (g_ptr_array_index (edit
->rules
, edit
->rules
->len
- 1));
1119 k
= g_new0 (syntax_keyword_t
, 1);
1120 g_ptr_array_add (last_rule
->keyword
, k
);
1121 if (strcmp (*a
, "whole") == 0)
1124 k
->whole_word_chars_left
= g_strdup (whole_left
);
1125 k
->whole_word_chars_right
= g_strdup (whole_right
);
1127 else if (strcmp (*a
, "wholeleft") == 0)
1130 k
->whole_word_chars_left
= g_strdup (whole_left
);
1132 else if (strcmp (*a
, "wholeright") == 0)
1135 k
->whole_word_chars_right
= g_strdup (whole_right
);
1138 if (strcmp (*a
, "linestart") == 0)
1144 if (strcmp (*a
, "whole") == 0)
1148 k
->keyword
= g_strdup (*a
++);
1149 subst_defines (edit
->defines
, a
, &args
[1024]);
1165 k
->color
= this_try_alloc_color_pair (fg
, bg
, attrs
);
1168 else if (*(args
[0]) == '#')
1170 /* do nothing for comment */
1172 else if (strcmp (args
[0], "file") == 0)
1176 else if (strcmp (args
[0], "define") == 0)
1183 argv
= g_tree_lookup (edit
->defines
, key
);
1185 mc_defines_destroy (NULL
, argv
, NULL
);
1187 key
= g_strdup (key
);
1189 argv
= g_new (char *, argc
- 1);
1190 g_tree_insert (edit
->defines
, key
, argv
);
1192 *argv
++ = g_strdup (*a
++);
1196 { /* anything else is an error */
1205 if (edit
->rules
->len
== 0)
1207 g_ptr_array_free (edit
->rules
, TRUE
);
1214 GString
*first_chars
;
1216 if (edit
->rules
== NULL
)
1219 first_chars
= g_string_sized_new (32);
1221 /* collect first character of keywords */
1222 for (i
= 0; i
< edit
->rules
->len
; i
++)
1226 g_string_set_size (first_chars
, 0);
1227 c
= CONTEXT_RULE (g_ptr_array_index (edit
->rules
, i
));
1229 g_string_append_c (first_chars
, (char) 1);
1230 for (j
= 1; j
< c
->keyword
->len
; j
++)
1232 syntax_keyword_t
*k
;
1234 k
= SYNTAX_KEYWORD (g_ptr_array_index (c
->keyword
, j
));
1235 g_string_append_c (first_chars
, k
->keyword
[0]);
1238 c
->keyword_first_chars
= g_strndup (first_chars
->str
, first_chars
->len
);
1241 g_string_free (first_chars
, TRUE
);
1247 /* --------------------------------------------------------------------------------------------- */
1249 /* returns -1 on file error, line number on error in file syntax */
1251 edit_read_syntax_file (WEdit
* edit
, GPtrArray
* pnames
, const char *syntax_file
,
1252 const char *editor_file
, const char *first_line
, const char *type
)
1255 char *args
[1024], *l
= NULL
;
1259 gboolean found
= FALSE
;
1261 f
= fopen (syntax_file
, "r");
1264 lib_file
= g_build_filename (mc_global
.share_data_dir
, "syntax", "Syntax", (char *) NULL
);
1265 f
= fopen (lib_file
, "r");
1276 if (read_one_line (&l
, f
) == 0)
1278 (void) get_args (l
, args
, 1023); /* Final NULL */
1279 if (args
[0] == NULL
)
1282 /* Looking for 'include ...' lines before first 'file ...' ones */
1283 if (!found
&& strcmp (args
[0], "include") == 0)
1288 if (!args
[1] || !(g
= open_include_file (args
[1])))
1296 /* looking for 'file ...' lines only */
1297 if (strcmp (args
[0], "file") != 0)
1302 /* must have two args or report error */
1303 if (!args
[1] || !args
[2])
1311 /* 1: just collecting a list of names of rule sets */
1312 g_ptr_array_add (pnames
, g_strdup (args
[2]));
1316 /* 2: rule set was explicitly specified by the caller */
1317 if (strcmp (type
, args
[2]) == 0)
1320 else if (editor_file
&& edit
)
1322 /* 3: auto-detect rule set from regular expressions */
1325 q
= mc_search (args
[1], DEFAULT_CHARSET
, editor_file
, MC_SEARCH_T_REGEX
);
1326 /* does filename match arg 1 ? */
1329 /* does first line match arg 3 ? */
1330 q
= mc_search (args
[3], DEFAULT_CHARSET
, first_line
, MC_SEARCH_T_REGEX
);
1337 syntax_type
= args
[2];
1338 line_error
= edit_read_syntax_rules (edit
, g
? g
: f
, args
, 1023);
1341 if (!error_file_name
) /* an included file */
1342 result
= line
+ line_error
;
1344 result
= line_error
;
1348 g_free (edit
->syntax_type
);
1349 edit
->syntax_type
= g_strdup (syntax_type
);
1350 /* if there are no rules then turn off syntax highlighting for speed */
1351 if (g
== NULL
&& edit
->rules
->len
== 1)
1355 r0
= CONTEXT_RULE (g_ptr_array_index (edit
->rules
, 0));
1356 if (r0
->keyword
->len
== 1 && !r0
->spelling
)
1358 edit_free_syntax_rules (edit
);
1377 /* --------------------------------------------------------------------------------------------- */
1380 get_first_editor_line (WEdit
* edit
)
1390 for (i
= 0; i
< sizeof (s
) - 1; i
++)
1392 s
[i
] = edit_buffer_get_byte (&edit
->buffer
, i
);
1400 s
[sizeof (s
) - 1] = '\0';
1406 /* --------------------------------------------------------------------------------------------- */
1407 /*** public functions ****************************************************************************/
1408 /* --------------------------------------------------------------------------------------------- */
1411 edit_get_syntax_color (WEdit
* edit
, off_t byte_index
)
1413 if (!tty_use_colors ())
1416 if (edit
->rules
!= NULL
&& byte_index
< edit
->buffer
.size
&& option_syntax_highlighting
)
1418 edit_get_rule (edit
, byte_index
);
1419 return translate_rule_to_color (edit
, &edit
->rule
);
1422 return EDITOR_NORMAL_COLOR
;
1425 /* --------------------------------------------------------------------------------------------- */
1428 edit_free_syntax_rules (WEdit
* edit
)
1433 if (edit
->defines
!= NULL
)
1434 destroy_defines (&edit
->defines
);
1436 if (edit
->rules
== NULL
)
1439 edit_get_rule (edit
, -1);
1440 MC_PTR_FREE (edit
->syntax_type
);
1442 g_ptr_array_foreach (edit
->rules
, (GFunc
) context_rule_free
, NULL
);
1443 g_ptr_array_free (edit
->rules
, TRUE
);
1445 g_slist_free_full (edit
->syntax_marker
, g_free
);
1446 edit
->syntax_marker
= NULL
;
1447 tty_color_free_all_tmp ();
1450 /* --------------------------------------------------------------------------------------------- */
1452 * Load rules into edit struct. Either edit or *pnames must be NULL. If
1453 * edit is NULL, a list of types will be stored into names. If type is
1454 * NULL, then the type will be selected according to the filename.
1455 * type must be edit->syntax_type or NULL
1458 edit_load_syntax (WEdit
* edit
, GPtrArray
* pnames
, const char *type
)
1463 if (option_auto_syntax
)
1470 saved_type
= g_strdup (type
); /* save edit->syntax_type */
1471 edit_free_syntax_rules (edit
);
1472 edit
->syntax_type
= saved_type
; /* restore edit->syntax_type */
1475 if (!tty_use_colors ())
1478 if (!option_syntax_highlighting
&& (pnames
== NULL
|| pnames
->len
== 0))
1481 if (edit
!= NULL
&& edit
->filename_vpath
== NULL
)
1484 f
= mc_config_get_full_path (EDIT_SYNTAX_FILE
);
1486 r
= edit_read_syntax_file (edit
, pnames
, f
, vfs_path_as_str (edit
->filename_vpath
),
1487 get_first_editor_line (edit
),
1488 option_auto_syntax
? NULL
: edit
->syntax_type
);
1490 r
= edit_read_syntax_file (NULL
, pnames
, f
, NULL
, "", NULL
);
1493 edit_free_syntax_rules (edit
);
1494 message (D_ERROR
, _("Load syntax file"),
1495 _("Cannot open file %s\n%s"), f
, unix_error_string (errno
));
1499 edit_free_syntax_rules (edit
);
1500 message (D_ERROR
, _("Load syntax file"),
1501 _("Error in file %s on line %d"), error_file_name
? error_file_name
: f
, r
);
1502 MC_PTR_FREE (error_file_name
);
1508 /* --------------------------------------------------------------------------------------------- */
1511 edit_get_syntax_type (const WEdit
* edit
)
1513 return edit
->syntax_type
;
1516 /* --------------------------------------------------------------------------------------------- */