message-view: bgo #727634 - Cannot copy build output
[anjuta.git] / libanjuta / anjuta-token-file.c
blobce8e51fab2160778b0e319b5e257056d15e62723
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * anjuta-token-file.c
4 * Copyright (C) Sébastien Granjoux 2009 <seb.sfo@free.fr>
5 *
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-file.h"
22 #include "anjuta-debug.h"
24 #include <glib-object.h>
26 #include <stdio.h>
27 #include <string.h>
29 /* Types declarations
30 *---------------------------------------------------------------------------*/
32 struct _AnjutaTokenFile
34 GObject parent;
36 GFile* file; /* Corresponding GFile */
38 AnjutaToken *content; /* Current file content */
40 AnjutaToken *save; /* List of memory block used */
42 gboolean dirty; /* Set when the file has been modified */
45 struct _AnjutaTokenFileClass
47 GObjectClass parent_class;
50 static GObjectClass *parent_class = NULL;
52 /* Helpers functions
53 *---------------------------------------------------------------------------*/
55 /* Private functions
56 *---------------------------------------------------------------------------*/
58 static AnjutaToken*
59 anjuta_token_file_find_position (AnjutaTokenFile *file, AnjutaToken *token)
61 AnjutaToken *start;
62 const gchar *pos;
63 const gchar *ptr;
64 const gchar *end;
66 if (token == NULL) return NULL;
68 if (anjuta_token_get_length (token) == 0)
70 AnjutaToken *last = anjuta_token_last (token);
72 for (; (token != NULL) && (token != last); token = anjuta_token_next (token))
74 if (anjuta_token_get_length (token) != 0) break;
77 if (anjuta_token_get_length (token) == 0) return NULL;
80 pos = anjuta_token_get_string (token);
81 for (start = file->content; start != NULL; start = anjuta_token_next (start))
83 guint len = anjuta_token_get_length (start);
85 if (len)
87 ptr = anjuta_token_get_string (start);
88 end = ptr + len;
90 if ((pos >= ptr) && (pos < end)) break;
93 if ((start != NULL) && (ptr != pos))
95 start = anjuta_token_split (start, pos - ptr);
96 start = anjuta_token_next (start);
99 return start;
102 /* Public functions
103 *---------------------------------------------------------------------------*/
105 AnjutaToken*
106 anjuta_token_file_load (AnjutaTokenFile *file, GError **error)
108 gchar *content;
109 gsize length;
111 anjuta_token_file_unload (file);
113 if (g_file_load_contents (file->file, NULL, &content, &length, NULL, error))
115 AnjutaToken *token;
117 file->save = anjuta_token_new_static (ANJUTA_TOKEN_FILE, NULL);
118 file->content = anjuta_token_new_static (ANJUTA_TOKEN_FILE, NULL);
120 token = anjuta_token_new_string_len (ANJUTA_TOKEN_FILE, content, length);
121 anjuta_token_prepend_child (file->save, token);
123 token = anjuta_token_new_static (ANJUTA_TOKEN_FILE, content);
124 anjuta_token_prepend_child (file->content, token);
125 file->dirty = FALSE;
128 return file->content;
131 gboolean
132 anjuta_token_file_unload (AnjutaTokenFile *file)
134 if (file->content != NULL) anjuta_token_free (file->content);
135 file->content = NULL;
137 if (file->save != NULL) anjuta_token_free (file->save);
138 file->save = NULL;
140 return TRUE;
143 gboolean
144 anjuta_token_file_save (AnjutaTokenFile *file, GError **error)
146 GFileOutputStream *stream;
147 gboolean ok = TRUE;
148 GError *err = NULL;
149 AnjutaToken *token;
151 stream = g_file_replace (file->file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &err);
152 if (stream == NULL)
154 if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
156 /* Perhaps parent directory is missing, try to create it */
157 GFile *parent = g_file_get_parent (file->file);
159 if (g_file_make_directory_with_parents (parent, NULL, NULL))
161 g_object_unref (parent);
162 g_clear_error (&err);
163 stream = g_file_replace (file->file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error);
164 if (stream == NULL) return FALSE;
166 else
168 g_object_unref (parent);
169 g_propagate_error (error, err);
171 return FALSE;
174 else
176 g_propagate_error (error, err);
177 return FALSE;
181 for (token = file->content; token != NULL; token = anjuta_token_next (token))
183 if (!(anjuta_token_get_flags (token) & ANJUTA_TOKEN_REMOVED) && (anjuta_token_get_length (token)))
185 if (g_output_stream_write (G_OUTPUT_STREAM (stream), anjuta_token_get_string (token), anjuta_token_get_length (token) * sizeof (char), NULL, error) < 0)
187 ok = FALSE;
188 break;
193 ok = ok && g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, NULL);
194 g_object_unref (stream);
195 file->dirty = FALSE;
197 return ok;
200 void
201 anjuta_token_file_move (AnjutaTokenFile *file, GFile *new_file)
203 if (file->file) g_object_unref (file->file);
204 file->file = new_file != NULL ? g_object_ref (new_file) : NULL;
205 file->dirty = new_file != NULL;
208 static void
209 remove_raw_token (AnjutaToken *token, gpointer user_data)
211 AnjutaTokenFile *file = (AnjutaTokenFile *)user_data;
213 if ((anjuta_token_get_length (token) > 0))
215 AnjutaToken *pos = anjuta_token_file_find_position (file, token);
216 guint len = anjuta_token_get_length (token);
218 if (pos != NULL)
220 while (len != 0)
222 guint flen = anjuta_token_get_length (pos);
223 if (len < flen)
225 pos = anjuta_token_split (pos, len);
226 flen = len;
228 pos = anjuta_token_free (pos);
229 len -= flen;
235 static AnjutaToken *
236 anjuta_token_file_remove_token (AnjutaTokenFile *file, AnjutaToken *token)
238 AnjutaToken *next = NULL;
240 if (token != NULL) next = anjuta_token_foreach_post_order (token, remove_raw_token, file);
241 file->dirty = TRUE;
243 return next;
247 * anjuta_token_file_update:
248 * @file: a #AnjutaTokenFile derived class object.
249 * @token: Token to update.
251 * Update the file with all changed token starting from @token. The function can
252 * return an error if the token is not in the file.
254 * Return value: %TRUE is the update is done without error.
256 gboolean
257 anjuta_token_file_update (AnjutaTokenFile *file, AnjutaToken *token)
259 AnjutaToken *prev;
260 AnjutaToken *next;
261 AnjutaToken *last;
262 guint added;
263 gchar *value;
265 /* Find all token needing an update */
267 /* Find following tokens */
268 for (last = token; last != NULL; last = anjuta_token_next (last))
270 /* Get all tokens in group */
271 last = anjuta_token_last (last);
273 gint flags = anjuta_token_get_flags (last);
274 if (!(flags & (ANJUTA_TOKEN_ADDED | ANJUTA_TOKEN_REMOVED))) break;
277 /* Find first modified token */
278 for (;;)
280 gint flags = anjuta_token_get_flags (token);
281 if (flags & (ANJUTA_TOKEN_ADDED | ANJUTA_TOKEN_REMOVED))
283 /* Check previous token */
284 for (prev = token; prev != NULL; prev = anjuta_token_previous (prev))
286 gint flags = anjuta_token_get_flags (prev);
287 if (!(flags & (ANJUTA_TOKEN_ADDED | ANJUTA_TOKEN_REMOVED))) break;
288 token = prev;
291 break;
293 if (token == last)
295 /* No changed */
296 return TRUE;
298 token = anjuta_token_next (token);
301 /* Find previous token */
302 for (prev = token; prev != NULL; prev = anjuta_token_previous (prev))
304 gint flags = anjuta_token_get_flags (prev);
305 if ((anjuta_token_get_length (prev) != 0) && !(flags & (ANJUTA_TOKEN_ADDED | ANJUTA_TOKEN_REMOVED))) break;
308 /* Delete removed token and compute length of added token */
309 added = 0;
310 for (next = token; (next != NULL) && (next != last);)
312 gint flags = anjuta_token_get_flags (next);
314 if (flags & ANJUTA_TOKEN_ADDED)
316 added += anjuta_token_get_length (next);
318 next = anjuta_token_next (next);
321 /* Add new token */
322 if (added != 0)
324 AnjutaToken *add;
325 AnjutaToken *start = NULL;
327 value = g_new (gchar, added);
328 add = anjuta_token_prepend_child (file->save, anjuta_token_new_string_len (ANJUTA_TOKEN_NAME, value, added));
330 /* Find token position */
331 if (prev != NULL)
333 start = anjuta_token_file_find_position (file, prev);
334 if (start != NULL) start = anjuta_token_split (start, anjuta_token_get_length (prev));
337 /* Insert token */
338 add = anjuta_token_new_static_len (ANJUTA_TOKEN_NAME, value, added);
339 if (start == NULL)
341 anjuta_token_prepend_child (file->content, add);
343 else
345 anjuta_token_insert_after (start, add);
349 for (next = token; (next != NULL) && (next != last);)
351 gint flags = anjuta_token_get_flags (next);
353 if (flags & ANJUTA_TOKEN_ADDED)
355 guint len = anjuta_token_get_length (next);
357 if (len > 0)
359 memcpy(value, anjuta_token_get_string (next), len);
360 anjuta_token_set_string (next, value, len);
361 value += len;
363 anjuta_token_clear_flags (next, ANJUTA_TOKEN_ADDED);
365 else if (flags & ANJUTA_TOKEN_REMOVED)
367 next = anjuta_token_file_remove_token (file, next);
368 continue;
370 next = anjuta_token_next (next);
373 file->dirty = TRUE;
375 return TRUE;
379 * anjuta_token_file_get_token_position:
380 * @file: #AnjutaTokenFile object
381 * @token: token
383 * Returns the position of the token in the file. This position is a number
384 * which doesn't correspond to a line number or a character but respect the
385 * order of token in the file.
387 * Returns: The position of the token or 0 if the token is not in the file.
389 gsize
390 anjuta_token_file_get_token_position (AnjutaTokenFile *file, AnjutaToken *token)
392 AnjutaToken *content;
393 const gchar *string;
394 gsize pos = 1;
399 string = anjuta_token_get_string (token);
400 if (string != NULL) break;
402 /* token is a group or an empty token, looks for group members or
403 * following token */
404 token = anjuta_token_next_after_children (token);
405 } while (token != NULL);
407 for (content = file->content; content != NULL; content = anjuta_token_next (content))
409 const gchar *start;
410 gsize len;
412 start = anjuta_token_get_string (content);
413 len = anjuta_token_get_length (content);
415 if ((string >= start) && ((string - start) < len))
417 pos += string - start;
418 break;
420 else
422 pos += len;
426 return content == NULL ? 0 : pos;
430 gboolean
431 anjuta_token_file_get_token_location (AnjutaTokenFile *file, AnjutaTokenFileLocation *location, AnjutaToken *token)
433 AnjutaTokenFileLocation loc = {NULL, 1, 1};
434 AnjutaToken *pos;
435 const gchar *target;
437 anjuta_token_dump (token);
440 target = anjuta_token_get_string (token);
441 if (target != NULL) break;
443 /* token is a group or an empty token, looks for group members or
444 * following token */
445 token = anjuta_token_next_after_children (token);
446 } while (token != NULL);
448 for (pos = file->content; pos != NULL; pos = anjuta_token_next (pos))
450 if (!(anjuta_token_get_flags (pos) & ANJUTA_TOKEN_REMOVED) && (anjuta_token_get_length (pos) > 0))
452 const gchar *ptr;
453 const gchar *end;
455 ptr = anjuta_token_get_string (pos);
456 end = ptr + anjuta_token_get_length (pos);
458 for (; ptr != end; ptr++)
460 if (*ptr == '\n')
462 /* New line */
463 loc.line++;
464 loc.column = 1;
466 else
468 loc.column++;
471 if (ptr == target)
473 if (location != NULL)
475 location->filename = file->file == NULL ? NULL : g_file_get_parse_name (file->file);
476 location->line = loc.line;
477 location->column = loc.column;
480 return TRUE;
486 return FALSE;
489 GFile*
490 anjuta_token_file_get_file (AnjutaTokenFile *file)
492 return file->file;
495 AnjutaToken*
496 anjuta_token_file_get_content (AnjutaTokenFile *file)
498 if (file->content == NULL)
500 anjuta_token_file_load (file, NULL);
503 return file->content;
506 gboolean
507 anjuta_token_file_is_dirty (AnjutaTokenFile *file)
509 return file->dirty;
512 /* GObject functions
513 *---------------------------------------------------------------------------*/
515 /* dispose is the first destruction step. It is used to unref object created
516 * with instance_init in order to break reference counting cycles. This
517 * function could be called several times. All function should still work
518 * after this call. It has to called its parents.*/
520 static void
521 anjuta_token_file_dispose (GObject *object)
523 AnjutaTokenFile *file = ANJUTA_TOKEN_FILE (object);
525 anjuta_token_file_unload (file);
527 if (file->file) g_object_unref (file->file);
528 file->file = NULL;
530 G_OBJECT_CLASS (parent_class)->dispose (object);
533 /* instance_init is the constructor. All functions should work after this
534 * call. */
536 static void
537 anjuta_token_file_instance_init (AnjutaTokenFile *file)
539 file->file = NULL;
540 file->content = NULL;
541 file->save = NULL;
544 /* class_init intialize the class itself not the instance */
546 static void
547 anjuta_token_file_class_init (AnjutaTokenFileClass * klass)
549 GObjectClass *gobject_class;
551 g_return_if_fail (klass != NULL);
553 parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
554 gobject_class = G_OBJECT_CLASS (klass);
556 gobject_class->dispose = anjuta_token_file_dispose;
559 GType
560 anjuta_token_file_get_type (void)
562 static GType type = 0;
564 if (!type)
566 static const GTypeInfo type_info =
568 sizeof (AnjutaTokenFileClass),
569 (GBaseInitFunc) NULL,
570 (GBaseFinalizeFunc) NULL,
571 (GClassInitFunc) anjuta_token_file_class_init,
572 (GClassFinalizeFunc) NULL,
573 NULL, /* class_data */
574 sizeof (AnjutaTokenFile),
575 0, /* n_preallocs */
576 (GInstanceInitFunc) anjuta_token_file_instance_init,
577 NULL /* value_table */
580 type = g_type_register_static (G_TYPE_OBJECT,
581 "AnjutaTokenFile", &type_info, 0);
584 return type;
588 /* Constructor & Destructor
589 *---------------------------------------------------------------------------*/
591 AnjutaTokenFile *
592 anjuta_token_file_new (GFile *gfile)
594 AnjutaTokenFile *file = g_object_new (ANJUTA_TOKEN_FILE_TYPE, NULL);
596 if (gfile) file->file = g_object_ref (gfile);
597 file->dirty = gfile != NULL;
599 return file;
602 void
603 anjuta_token_file_free (AnjutaTokenFile *tfile)
605 g_object_unref (tfile);