1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 * Copyright (C) Sébastien Granjoux 2009 <seb.sfo@free.fr>
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "anjuta-token-list.h"
22 #include "libanjuta/anjuta-debug.h"
28 *---------------------------------------------------------------------------*/
30 typedef struct _AnjutaTokenStyleSeparator AnjutaTokenStyleSeparator
;
32 struct _AnjutaTokenStyleSeparator
39 struct _AnjutaTokenStyle
42 GHashTable
*separator
;
43 AnjutaTokenStyle
*base
;
47 *---------------------------------------------------------------------------*/
50 free_separator (AnjutaTokenStyleSeparator
*sep
, gpointer user_data
)
53 g_slice_free (AnjutaTokenStyleSeparator
, sep
);
57 free_separator_list (guint key
, GList
*value
, gpointer user_data
)
59 /* Free list elements */
60 g_list_foreach (value
, (GFunc
)free_separator
, NULL
);
64 static AnjutaTokenStyleSeparator
*
65 anjuta_token_style_insert_separator (AnjutaTokenStyle
*style
, guint key
, const gchar
*value
)
69 GList
*sibling
= NULL
;
70 AnjutaTokenStyleSeparator
*sep
;
73 /* Look the separator is already registered */
74 list
= (GList
*)g_hash_table_lookup (style
->separator
, GINT_TO_POINTER (key
));
77 for (sibling
= list
; sibling
!= NULL
; sibling
= g_list_next(sibling
))
79 sep
= (AnjutaTokenStyleSeparator
*)sibling
->data
;
81 /* Keep the first separator with count = 1, to insert the new one if
83 if ((last
== NULL
) && (sep
->count
== 1)) last
= sibling
;
87 if (sep
->value
== NULL
)
93 else if ((sep
->value
!= NULL
) && (strcmp (sep
->value
, value
) == 0))
103 /* Increment the separator count, Move it if needed */
104 for (last
= g_list_previous (sibling
); last
!= NULL
; last
= g_list_previous (sibling
))
106 if (((AnjutaTokenStyleSeparator
*)sibling
->data
)->count
>= ((AnjutaTokenStyleSeparator
*)last
->data
)->count
)
108 last
->next
= sibling
->next
;
109 sibling
->next
= last
;
110 sibling
->prev
= last
->prev
;
111 last
->prev
= sibling
;
121 /* Update the list head */
123 g_hash_table_replace (style
->separator
, GINT_TO_POINTER (key
), list
);
126 return (AnjutaTokenStyleSeparator
*)sibling
->data
;
130 /* Create a new separator */
131 sep
= g_slice_new0 (AnjutaTokenStyleSeparator
);
133 sep
->value
= g_strdup (value
);
134 sep
->eol
= value
== NULL
? FALSE
: strchr (value
, '\n') != NULL
;
135 list
= g_list_insert_before (list
, last
, sep
);
136 g_hash_table_replace (style
->separator
, GINT_TO_POINTER (key
), list
);
142 static AnjutaTokenStyleSeparator
*
143 anjuta_token_style_insert_separator_between (AnjutaTokenStyle
*style
, gint next
, gint prev
, const gchar
*value
)
145 return anjuta_token_style_insert_separator (style
, ((guint
)prev
& 0xFFFF) | (((guint
)next
& 0xFFFF) << 16), value
);
149 anjuta_token_style_lookup (AnjutaTokenStyle
*style
, AnjutaTokenType type
, gboolean eol
)
153 list
= g_hash_table_lookup (style
->separator
, GINT_TO_POINTER (type
));
154 if ((list
== NULL
) && (style
->base
!= NULL
))
156 return anjuta_token_style_lookup (style
->base
, type
, eol
);
160 return anjuta_token_new_string (ANJUTA_TOKEN_NAME
, ((AnjutaTokenStyleSeparator
*)list
->data
)->value
);
164 /* Public style functions
165 *---------------------------------------------------------------------------*/
168 anjuta_token_style_update (AnjutaTokenStyle
*style
, AnjutaToken
*list
)
171 AnjutaToken
*next_token
;
174 guint line_width
= 0;
177 /* Initialize first line width */
178 for (token
= list
; token
!= NULL
; token
= anjuta_token_previous (token
))
184 value
= anjuta_token_evaluate (token
);
187 eol
= strrchr (value
, '\n');
188 len
= strlen (value
);
194 line_width
= value
+ len
- eol
;
201 for (token
= anjuta_token_first_item (list
); token
!= NULL
; token
= next_token
)
208 next_token
= anjuta_token_next_item (token
);
209 type
= anjuta_token_get_type (token
);
210 next
= next_token
== NULL
? 0 : anjuta_token_get_type (next_token
);
212 value
= anjuta_token_evaluate (token
);
213 if (value
== NULL
) continue;
215 len
= strlen (value
);
216 eol
= strrchr (value
, '\n');
217 if (eol
!= NULL
) len
-= (eol
- value
);
224 case ANJUTA_TOKEN_START
:
225 case ANJUTA_TOKEN_LAST
:
226 case ANJUTA_TOKEN_NEXT
:
237 value
= anjuta_token_evaluate (token
);
238 anjuta_token_style_insert_separator_between (style
, 0, type
, value
);
239 if (type
== ANJUTA_TOKEN_NEXT
)
241 anjuta_token_style_insert_separator_between (style
, next
, prev
, value
);
242 anjuta_token_style_insert_separator_between (style
, next
, ANJUTA_TOKEN_ANY
, value
);
243 anjuta_token_style_insert_separator_between (style
, ANJUTA_TOKEN_ANY
, prev
, value
);
253 if ((sep_count
> 1) && (line_width
> style
->max_width
))
255 style
->max_width
= line_width
;
264 anjuta_token_style_format (AnjutaTokenStyle
*style
, AnjutaToken
*list
)
271 /* Find following tokens */
272 for (last
= list
; last
!= NULL
; last
= anjuta_token_next (last
))
274 /* Get all tokens in group */
275 last
= anjuta_token_last (last
);
277 gint flags
= anjuta_token_get_flags (last
);
278 if (!(flags
& (ANJUTA_TOKEN_ADDED
| ANJUTA_TOKEN_REMOVED
))) break;
281 /* Find previous token */
282 for (prev
= list
; prev
!= NULL
; prev
= anjuta_token_previous (prev
))
284 gint flags
= anjuta_token_get_flags (prev
);
285 if ((anjuta_token_get_length (prev
) != 0) && !(flags
& (ANJUTA_TOKEN_ADDED
| ANJUTA_TOKEN_REMOVED
))) break;
289 for (item
= list
; (item
!= NULL
) && (item
!= last
); item
= anjuta_token_next (item
))
291 if ((anjuta_token_get_flags (item
) & ANJUTA_TOKEN_ADDED
) &&
292 !(anjuta_token_get_flags (item
) & ANJUTA_TOKEN_REMOVED
))
294 switch (anjuta_token_get_type (item
))
296 case ANJUTA_TOKEN_START
:
297 text
= anjuta_token_style_lookup (style
, ANJUTA_TOKEN_START
, FALSE
);
298 anjuta_token_set_flags (text
, ANJUTA_TOKEN_ADDED
);
299 anjuta_token_insert_after (item
, text
);
300 anjuta_token_merge (item
, text
);
303 case ANJUTA_TOKEN_NEXT
:
304 text
= anjuta_token_style_lookup (style
, ANJUTA_TOKEN_NEXT
, FALSE
);
305 anjuta_token_set_flags (text
, ANJUTA_TOKEN_ADDED
);
306 anjuta_token_insert_after (item
, text
);
307 anjuta_token_merge (item
, text
);
310 case ANJUTA_TOKEN_LAST
:
311 text
= anjuta_token_style_lookup (style
, ANJUTA_TOKEN_LAST
, FALSE
);
312 anjuta_token_set_flags (text
, ANJUTA_TOKEN_ADDED
);
313 anjuta_token_insert_after (item
, text
);
314 anjuta_token_merge (item
, text
);
325 *---------------------------------------------------------------------------*/
328 * anjuta_token_first_word:
329 * @list: a #AnjutaToken object being a list
331 * Get the first word of the list. A word is an item in the list which is not
332 * a space or a separator.
334 * Return value: A #AnjutaToken representing the first word or NULL.
337 anjuta_token_first_word (AnjutaToken
*list
)
341 for (item
= anjuta_token_first_item (list
); item
!= NULL
; item
= anjuta_token_next_item (item
))
343 if (anjuta_token_list (item
) != list
)
348 switch (anjuta_token_get_type (item
))
350 case ANJUTA_TOKEN_START
:
351 case ANJUTA_TOKEN_NEXT
:
353 case ANJUTA_TOKEN_LAST
:
357 if (anjuta_token_is_empty (item
)) continue;
367 anjuta_token_next_word (AnjutaToken
*item
)
371 for (next
= anjuta_token_next_item (item
); next
!= NULL
; next
= anjuta_token_next_item (next
))
373 if (anjuta_token_list (item
) != anjuta_token_list (next
))
378 switch (anjuta_token_get_type (next
))
380 case ANJUTA_TOKEN_START
:
381 case ANJUTA_TOKEN_NEXT
:
383 case ANJUTA_TOKEN_LAST
:
387 if (anjuta_token_is_empty (next
)) continue;
397 anjuta_token_nth_word (AnjutaToken
*list
, guint n
)
400 gboolean no_item
= TRUE
;
402 for (item
= anjuta_token_first_item (list
); item
!= NULL
; item
= anjuta_token_next_item (item
))
404 if (anjuta_token_list (item
) != list
)
409 switch (anjuta_token_get_type (item
))
411 case ANJUTA_TOKEN_START
:
413 case ANJUTA_TOKEN_NEXT
:
416 if (n
== 0) return NULL
;
421 case ANJUTA_TOKEN_LAST
:
424 if (n
== 0) return item
;
435 anjuta_token_replace_nth_word (AnjutaToken
*list
, guint n
, AnjutaToken
*item
)
438 gboolean no_item
= TRUE
;
440 token
= anjuta_token_first_item (list
);
443 token
= anjuta_token_insert_after (token
, anjuta_token_new_static (ANJUTA_TOKEN_LAST
| ANJUTA_TOKEN_ADDED
, NULL
));
444 anjuta_token_merge (list
, token
);
451 switch (anjuta_token_get_type (token
))
453 case ANJUTA_TOKEN_LAST
:
459 token
= anjuta_token_insert_before (token
, item
);
463 token
= anjuta_token_insert_before (token
, anjuta_token_new_static (ANJUTA_TOKEN_NEXT
| ANJUTA_TOKEN_ADDED
, NULL
));
466 case ANJUTA_TOKEN_NEXT
:
472 token
= anjuta_token_insert_before (token
, item
);
478 case ANJUTA_TOKEN_ITEM
:
482 anjuta_token_set_flags (token
, ANJUTA_TOKEN_REMOVED
);
483 token
= anjuta_token_insert_before (token
, item
);
492 next
= anjuta_token_next_item (token
);
495 token
= anjuta_token_insert_after (token
, anjuta_token_new_static (ANJUTA_TOKEN_LAST
| ANJUTA_TOKEN_ADDED
, NULL
));
496 anjuta_token_merge (list
, token
);
506 anjuta_token_insert_word_before (AnjutaToken
*list
, AnjutaToken
*sibling
, AnjutaToken
*item
)
510 if (list
== NULL
) list
= anjuta_token_list (sibling
);
512 for (token
= anjuta_token_first_item (list
); token
!= NULL
;)
516 switch (anjuta_token_get_type (token
))
518 case ANJUTA_TOKEN_LAST
:
519 anjuta_token_insert_before (token
, anjuta_token_new_static (ANJUTA_TOKEN_NEXT
| ANJUTA_TOKEN_ADDED
, NULL
));
520 anjuta_token_insert_before (token
, item
);
522 case ANJUTA_TOKEN_START
:
523 if (token
== sibling
)
525 anjuta_token_insert_after (token
, anjuta_token_new_static (ANJUTA_TOKEN_NEXT
| ANJUTA_TOKEN_ADDED
, NULL
));
526 anjuta_token_insert_after (token
, item
);
530 case ANJUTA_TOKEN_NEXT
:
531 if (token
== sibling
)
533 token
= anjuta_token_insert_before (token
, item
);
534 anjuta_token_insert_before (token
, anjuta_token_new_static (ANJUTA_TOKEN_NEXT
| ANJUTA_TOKEN_ADDED
, NULL
));
539 if (token
== sibling
)
541 anjuta_token_insert_before (token
, item
);
542 anjuta_token_insert_before (token
, anjuta_token_new_static (ANJUTA_TOKEN_NEXT
| ANJUTA_TOKEN_ADDED
, NULL
));
548 next
= anjuta_token_next_item (token
);
551 token
= anjuta_token_insert_after (token
, anjuta_token_new_static (ANJUTA_TOKEN_NEXT
| ANJUTA_TOKEN_ADDED
, NULL
));
552 anjuta_token_insert_after (token
, item
);
558 anjuta_token_prepend_items (list
, item
);
564 anjuta_token_insert_word_after (AnjutaToken
*list
, AnjutaToken
*sibling
, AnjutaToken
*item
)
568 if (list
== NULL
) list
= anjuta_token_list (sibling
);
570 for (token
= anjuta_token_first_item (list
); token
!= NULL
;)
574 next
= anjuta_token_next_item (token
);
576 switch (anjuta_token_get_type (token
))
578 case ANJUTA_TOKEN_LAST
:
579 anjuta_token_insert_before (token
, anjuta_token_new_static (ANJUTA_TOKEN_NEXT
| ANJUTA_TOKEN_ADDED
, NULL
));
580 anjuta_token_insert_before (token
, item
);
582 case ANJUTA_TOKEN_START
:
583 if ((sibling
== NULL
) || (token
== sibling
))
585 if (next
!= NULL
) anjuta_token_insert_after (token
, anjuta_token_new_static (ANJUTA_TOKEN_NEXT
| ANJUTA_TOKEN_ADDED
, NULL
));
586 anjuta_token_insert_after (token
, item
);
590 case ANJUTA_TOKEN_NEXT
:
591 if (token
== sibling
)
593 token
= anjuta_token_insert_after (token
, item
);
594 anjuta_token_insert_after (token
, anjuta_token_new_static (ANJUTA_TOKEN_NEXT
| ANJUTA_TOKEN_ADDED
, NULL
));
599 if (token
== sibling
)
601 token
= anjuta_token_insert_after (token
, anjuta_token_new_static (ANJUTA_TOKEN_NEXT
| ANJUTA_TOKEN_ADDED
, NULL
));
602 anjuta_token_insert_after (token
, item
);
610 token
= anjuta_token_insert_after (token
, anjuta_token_new_static (ANJUTA_TOKEN_NEXT
| ANJUTA_TOKEN_ADDED
, NULL
));
611 anjuta_token_insert_after (token
, item
);
617 anjuta_token_prepend_items (list
, item
);
623 anjuta_token_remove_word (AnjutaToken
*token
)
627 anjuta_token_set_flags (token
, ANJUTA_TOKEN_REMOVED
);
628 next
= anjuta_token_next_item (token
);
629 if ((next
!= NULL
) && (anjuta_token_list (token
) == anjuta_token_list (next
)) && (anjuta_token_get_type (next
) == ANJUTA_TOKEN_NEXT
))
631 /* Remove following separator */
632 anjuta_token_set_flags (next
, ANJUTA_TOKEN_REMOVED
);
636 next
= anjuta_token_previous_item (token
);
637 if ((next
!= NULL
) && (anjuta_token_list (token
) == anjuta_token_list (next
)) && (anjuta_token_get_type (next
) == ANJUTA_TOKEN_NEXT
))
639 /* Remove previous separator */
640 anjuta_token_set_flags (next
, ANJUTA_TOKEN_REMOVED
);
652 * anjuta_token_remove_list:
653 * @token: a #AnjutaToken corresponding to a list
655 * Remove a complete list of token.
657 * Return value: A #AnjutaToken representing the following token
660 anjuta_token_remove_list (AnjutaToken
*list
)
665 anjuta_token_set_flags (list
, ANJUTA_TOKEN_REMOVED
);
667 prev
= anjuta_token_previous_item (list
);
670 if (anjuta_token_get_type (prev
) == ANJUTA_TOKEN_EOL
)
672 /* Remove line above if empty */
673 AnjutaToken
*prev_prev
= anjuta_token_previous_item (prev
);
675 if ((prev_prev
== NULL
) || (anjuta_token_get_type (prev_prev
) == ANJUTA_TOKEN_EOL
) || (anjuta_token_get_type (prev_prev
) == ANJUTA_TOKEN_COMMENT
))
677 anjuta_token_set_flags (prev
, ANJUTA_TOKEN_REMOVED
);
680 else if (anjuta_token_get_type (prev
) == ANJUTA_TOKEN_COMMENT
)
682 /* Remove comment above if there is an empty line after it */
685 prev
= anjuta_token_previous_item (prev
);
687 while ((prev
!= NULL
) && (anjuta_token_get_type (prev
) == ANJUTA_TOKEN_COMMENT
));
689 if ((prev
!= NULL
) && (anjuta_token_get_type (prev
) == ANJUTA_TOKEN_EOL
))
694 anjuta_token_set_flags (prev
, ANJUTA_TOKEN_REMOVED
);
695 prev
= anjuta_token_previous_item (prev
);
697 while ((prev
!= NULL
) && (anjuta_token_get_type (prev
) == ANJUTA_TOKEN_COMMENT
));
702 next
= anjuta_token_next_item (list
);
705 if (anjuta_token_get_type (next
) == ANJUTA_TOKEN_EOL
)
707 anjuta_token_set_flags (next
, ANJUTA_TOKEN_REMOVED
);
709 next
= anjuta_token_next_item (next
);
716 anjuta_token_insert_token_list (gboolean after
, AnjutaToken
*pos
,...)
718 AnjutaToken
*first
= NULL
;
723 va_start (args
, pos
);
725 for (type
= va_arg (args
, gint
); type
!= 0; type
= va_arg (args
, gint
))
727 gchar
*string
= va_arg (args
, gchar
*);
732 pos
= token
= anjuta_token_insert_after (pos
, anjuta_token_new_string (type
| ANJUTA_TOKEN_ADDED
, string
));
736 token
= anjuta_token_insert_before (pos
, anjuta_token_new_string (type
| ANJUTA_TOKEN_ADDED
, string
));
738 if (first
== NULL
) first
= token
;
742 anjuta_token_merge ((AnjutaToken
*)group
->data
, token
);
749 case ANJUTA_TOKEN_LIST
:
752 group
= g_list_delete_link (group
, group
);
755 group
= g_list_prepend (group
, token
);
766 anjuta_token_find_type (AnjutaToken
*list
, gint flags
, AnjutaTokenType
* types
)
769 AnjutaToken
*last
= NULL
;
771 for (tok
= list
; tok
!= NULL
; tok
= anjuta_token_next (tok
))
773 AnjutaTokenType
*type
;
774 for (type
= types
; *type
!= 0; type
++)
776 if (anjuta_token_get_type (tok
) == *type
)
779 if (flags
& ANJUTA_TOKEN_SEARCH_NOT
) break;
780 if (!(flags
& ANJUTA_TOKEN_SEARCH_LAST
)) break;
783 if ((flags
& ANJUTA_TOKEN_SEARCH_NOT
) && (*type
== 0)) break;
790 anjuta_token_skip_comment (AnjutaToken
*token
)
792 if (token
== NULL
) return NULL
;
798 AnjutaToken
*next
= anjuta_token_next (token
);
800 if (next
== NULL
) return token
;
802 switch (anjuta_token_get_type (token
))
804 case ANJUTA_TOKEN_FILE
:
805 case ANJUTA_TOKEN_SPACE
:
808 case ANJUTA_TOKEN_COMMENT
:
819 AnjutaToken
*next
= anjuta_token_next (token
);
821 if (next
== NULL
) return token
;
823 if (anjuta_token_get_type (token
) == ANJUTA_TOKEN_EOL
) break;
828 /* Constructor & Destructor
829 *---------------------------------------------------------------------------*/
832 anjuta_token_style_new (const gchar
*start
, const gchar
*next
, const gchar
*eol
, const gchar
*last
, guint max_width
)
834 AnjutaTokenStyle
*style
;
836 style
= g_slice_new0 (AnjutaTokenStyle
);
837 style
->max_width
= max_width
;
839 style
->separator
= g_hash_table_new (g_direct_hash
, NULL
);
840 anjuta_token_style_insert_separator (style
, ANJUTA_TOKEN_START
, start
);
841 anjuta_token_style_insert_separator (style
, ANJUTA_TOKEN_NEXT
, next
);
842 anjuta_token_style_insert_separator (style
, ANJUTA_TOKEN_NEXT
, eol
);
843 anjuta_token_style_insert_separator (style
, ANJUTA_TOKEN_LAST
, last
);
849 anjuta_token_style_new_from_base (AnjutaTokenStyle
*base
)
851 AnjutaTokenStyle
*style
;
853 style
= g_slice_new0 (AnjutaTokenStyle
);
854 style
->max_width
= base
->max_width
;
857 style
->separator
= g_hash_table_new (g_direct_hash
, NULL
);
863 anjuta_token_style_free (AnjutaTokenStyle
*style
)
865 g_hash_table_foreach (style
->separator
, (GHFunc
)free_separator_list
, NULL
);
866 g_hash_table_destroy (style
->separator
);
867 g_slice_free (AnjutaTokenStyle
, style
);