1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2  *
3  * plumatextregion.h - GtkTextMark based region utility functions
4  *
5  * This file is part of the GtkSourceView widget
6  *
7  * Copyright (C) 2002 Gustavo Giráldez <gustavo.giraldez@gmx.net>
8  * Copyright (C) 2012-2021 MATE Developers
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
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.
19  *
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., 51 Franklin St, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  */
25 
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29 
30 #include <glib.h>
31 
32 #include "plumatextregion.h"
33 
34 
35 #undef ENABLE_DEBUG
36 /*
37 #define ENABLE_DEBUG
38 */
39 
40 #ifdef ENABLE_DEBUG
41 #define DEBUG(x) (x)
42 #else
43 #define DEBUG(x)
44 #endif
45 
46 typedef struct _Subregion {
47 	GtkTextMark *start;
48 	GtkTextMark *end;
49 } Subregion;
50 
51 struct _PlumaTextRegion {
52 	GtkTextBuffer *buffer;
53 	GList         *subregions;
54 	guint32        time_stamp;
55 };
56 
57 typedef struct _PlumaTextRegionIteratorReal PlumaTextRegionIteratorReal;
58 
59 struct _PlumaTextRegionIteratorReal {
60 	PlumaTextRegion *region;
61 	guint32        region_time_stamp;
62 
63 	GList         *subregions;
64 };
65 
66 
67 /* ----------------------------------------------------------------------
68    Private interface
69    ---------------------------------------------------------------------- */
70 
71 /* Find and return a subregion node which contains the given text
72    iter.  If left_side is TRUE, return the subregion which contains
73    the text iter or which is the leftmost; else return the rightmost
74    subregion */
75 static GList *
find_nearest_subregion(PlumaTextRegion * region,const GtkTextIter * iter,GList * begin,gboolean leftmost,gboolean include_edges)76 find_nearest_subregion (PlumaTextRegion     *region,
77 			const GtkTextIter *iter,
78 			GList             *begin,
79 			gboolean           leftmost,
80 			gboolean           include_edges)
81 {
82 	GList *l, *retval;
83 
84 	g_return_val_if_fail (region != NULL && iter != NULL, NULL);
85 
86 	if (!begin)
87 		begin = region->subregions;
88 
89 	if (begin)
90 		retval = begin->prev;
91 	else
92 		retval = NULL;
93 
94 	for (l = begin; l; l = l->next) {
95 		GtkTextIter sr_iter;
96 		Subregion *sr = l->data;
97 		gint cmp;
98 
99 		if (!leftmost) {
100 			gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_iter, sr->end);
101 			cmp = gtk_text_iter_compare (iter, &sr_iter);
102 			if (cmp < 0 || (cmp == 0 && include_edges)) {
103 				retval = l;
104 				break;
105 			}
106 
107 		} else {
108 			gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_iter, sr->start);
109 			cmp = gtk_text_iter_compare (iter, &sr_iter);
110 			if (cmp > 0 || (cmp == 0 && include_edges))
111 				retval = l;
112 			else
113 				break;
114 		}
115 	}
116 	return retval;
117 }
118 
119 /* ----------------------------------------------------------------------
120    Public interface
121    ---------------------------------------------------------------------- */
122 
123 PlumaTextRegion *
pluma_text_region_new(GtkTextBuffer * buffer)124 pluma_text_region_new (GtkTextBuffer *buffer)
125 {
126 	PlumaTextRegion *region;
127 
128 	g_return_val_if_fail (buffer != NULL, NULL);
129 
130 	region = g_new (PlumaTextRegion, 1);
131 	region->buffer = buffer;
132 	region->subregions = NULL;
133 	region->time_stamp = 0;
134 
135 	return region;
136 }
137 
138 void
pluma_text_region_destroy(PlumaTextRegion * region,gboolean delete_marks)139 pluma_text_region_destroy (PlumaTextRegion *region, gboolean delete_marks)
140 {
141 	g_return_if_fail (region != NULL);
142 
143 	while (region->subregions) {
144 		Subregion *sr = region->subregions->data;
145 		if (delete_marks) {
146 			gtk_text_buffer_delete_mark (region->buffer, sr->start);
147 			gtk_text_buffer_delete_mark (region->buffer, sr->end);
148 		}
149 		g_free (sr);
150 		region->subregions = g_list_delete_link (region->subregions,
151 							 region->subregions);
152 	}
153 	region->buffer = NULL;
154 	region->time_stamp = 0;
155 
156 	g_free (region);
157 }
158 
159 GtkTextBuffer *
pluma_text_region_get_buffer(PlumaTextRegion * region)160 pluma_text_region_get_buffer (PlumaTextRegion *region)
161 {
162 	g_return_val_if_fail (region != NULL, NULL);
163 
164 	return region->buffer;
165 }
166 
167 static void
pluma_text_region_clear_zero_length_subregions(PlumaTextRegion * region)168 pluma_text_region_clear_zero_length_subregions (PlumaTextRegion *region)
169 {
170 	GtkTextIter start, end;
171 	GList *node;
172 
173 	g_return_if_fail (region != NULL);
174 
175 	for (node = region->subregions; node; ) {
176 		Subregion *sr = node->data;
177 		gtk_text_buffer_get_iter_at_mark (region->buffer, &start, sr->start);
178 		gtk_text_buffer_get_iter_at_mark (region->buffer, &end, sr->end);
179 		if (gtk_text_iter_equal (&start, &end)) {
180 			gtk_text_buffer_delete_mark (region->buffer, sr->start);
181 			gtk_text_buffer_delete_mark (region->buffer, sr->end);
182 			g_free (sr);
183 			if (node == region->subregions)
184 				region->subregions = node = g_list_delete_link (node, node);
185 			else
186 				node = g_list_delete_link (node, node);
187 
188 			++region->time_stamp;
189 
190 		} else {
191 			node = node->next;
192 		}
193 	}
194 }
195 
196 void
pluma_text_region_add(PlumaTextRegion * region,const GtkTextIter * _start,const GtkTextIter * _end)197 pluma_text_region_add (PlumaTextRegion     *region,
198 		     const GtkTextIter *_start,
199 		     const GtkTextIter *_end)
200 {
201 	GList *start_node, *end_node;
202 	GtkTextIter start, end;
203 
204 	g_return_if_fail (region != NULL && _start != NULL && _end != NULL);
205 
206 	start = *_start;
207 	end = *_end;
208 
209 	DEBUG (g_print ("---\n"));
210 	DEBUG (pluma_text_region_debug_print (region));
211 	DEBUG (g_message ("region_add (%d, %d)",
212 			  gtk_text_iter_get_offset (&start),
213 			  gtk_text_iter_get_offset (&end)));
214 
215 	gtk_text_iter_order (&start, &end);
216 
217 	/* don't add zero-length regions */
218 	if (gtk_text_iter_equal (&start, &end))
219 		return;
220 
221 	/* find bounding subregions */
222 	start_node = find_nearest_subregion (region, &start, NULL, FALSE, TRUE);
223 	end_node = find_nearest_subregion (region, &end, start_node, TRUE, TRUE);
224 
225 	if (start_node == NULL || end_node == NULL || end_node == start_node->prev) {
226 		/* create the new subregion */
227 		Subregion *sr = g_new0 (Subregion, 1);
228 		sr->start = gtk_text_buffer_create_mark (region->buffer, NULL, &start, TRUE);
229 		sr->end = gtk_text_buffer_create_mark (region->buffer, NULL, &end, FALSE);
230 
231 		if (start_node == NULL) {
232 			/* append the new region */
233 			region->subregions = g_list_append (region->subregions, sr);
234 
235 		} else if (end_node == NULL) {
236 			/* prepend the new region */
237 			region->subregions = g_list_prepend (region->subregions, sr);
238 
239 		} else {
240 			/* we are in the middle of two subregions */
241 			region->subregions = g_list_insert_before (region->subregions,
242 								   start_node, sr);
243 		}
244 	}
245 	else {
246 		GtkTextIter iter;
247 		Subregion *sr = start_node->data;
248 		if (start_node != end_node) {
249 			/* we need to merge some subregions */
250 			GList *l = start_node->next;
251 			Subregion *q;
252 
253 			gtk_text_buffer_delete_mark (region->buffer, sr->end);
254 			while (l != end_node) {
255 				q = l->data;
256 				gtk_text_buffer_delete_mark (region->buffer, q->start);
257 				gtk_text_buffer_delete_mark (region->buffer, q->end);
258 				g_free (q);
259 				l = g_list_delete_link (l, l);
260 			}
261 			q = l->data;
262 			gtk_text_buffer_delete_mark (region->buffer, q->start);
263 			sr->end = q->end;
264 			g_free (q);
265 			l = g_list_delete_link (l, l);
266 		}
267 		/* now move marks if that action expands the region */
268 		gtk_text_buffer_get_iter_at_mark (region->buffer, &iter, sr->start);
269 		if (gtk_text_iter_compare (&iter, &start) > 0)
270 			gtk_text_buffer_move_mark (region->buffer, sr->start, &start);
271 		gtk_text_buffer_get_iter_at_mark (region->buffer, &iter, sr->end);
272 		if (gtk_text_iter_compare (&iter, &end) < 0)
273 			gtk_text_buffer_move_mark (region->buffer, sr->end, &end);
274 	}
275 
276 	++region->time_stamp;
277 
278 	DEBUG (pluma_text_region_debug_print (region));
279 }
280 
281 void
pluma_text_region_subtract(PlumaTextRegion * region,const GtkTextIter * _start,const GtkTextIter * _end)282 pluma_text_region_subtract (PlumaTextRegion     *region,
283 			  const GtkTextIter *_start,
284 			  const GtkTextIter *_end)
285 {
286 	GList *start_node, *end_node, *node;
287 	GtkTextIter sr_start_iter, sr_end_iter;
288 	gboolean done;
289 	gboolean start_is_outside, end_is_outside;
290 	Subregion *sr;
291 	GtkTextIter start, end;
292 
293 	g_return_if_fail (region != NULL && _start != NULL && _end != NULL);
294 
295 	start = *_start;
296 	end = *_end;
297 
298 	DEBUG (g_print ("---\n"));
299 	DEBUG (pluma_text_region_debug_print (region));
300 	DEBUG (g_message ("region_substract (%d, %d)",
301 			  gtk_text_iter_get_offset (&start),
302 			  gtk_text_iter_get_offset (&end)));
303 
304 	gtk_text_iter_order (&start, &end);
305 
306 	/* find bounding subregions */
307 	start_node = find_nearest_subregion (region, &start, NULL, FALSE, FALSE);
308 	end_node = find_nearest_subregion (region, &end, start_node, TRUE, FALSE);
309 
310 	/* easy case first */
311 	if (start_node == NULL || end_node == NULL || end_node == start_node->prev)
312 		return;
313 
314 	/* deal with the start point */
315 	start_is_outside = end_is_outside = FALSE;
316 
317 	sr = start_node->data;
318 	gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_start_iter, sr->start);
319 	gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_end_iter, sr->end);
320 
321 	if (gtk_text_iter_in_range (&start, &sr_start_iter, &sr_end_iter) &&
322 	    !gtk_text_iter_equal (&start, &sr_start_iter)) {
323 		/* the starting point is inside the first subregion */
324 		if (gtk_text_iter_in_range (&end, &sr_start_iter, &sr_end_iter) &&
325 		    !gtk_text_iter_equal (&end, &sr_end_iter)) {
326 			/* the ending point is also inside the first
327                            subregion: we need to split */
328 			Subregion *new_sr = g_new0 (Subregion, 1);
329 			new_sr->end = sr->end;
330 			new_sr->start = gtk_text_buffer_create_mark (region->buffer,
331 								     NULL, &end, TRUE);
332 			start_node = g_list_insert_before (start_node, start_node->next, new_sr);
333 
334 			sr->end = gtk_text_buffer_create_mark (region->buffer,
335 							       NULL, &start, FALSE);
336 
337 			/* no further processing needed */
338 			DEBUG (g_message ("subregion splitted"));
339 
340 			return;
341 		} else {
342 			/* the ending point is outside, so just move
343                            the end of the subregion to the starting point */
344 			gtk_text_buffer_move_mark (region->buffer, sr->end, &start);
345 		}
346 	} else {
347 		/* the starting point is outside (and so to the left)
348                    of the first subregion */
349 		DEBUG (g_message ("start is outside"));
350 
351 		start_is_outside = TRUE;
352 	}
353 
354 	/* deal with the end point */
355 	if (start_node != end_node) {
356 		sr = end_node->data;
357 		gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_start_iter, sr->start);
358 		gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_end_iter, sr->end);
359 	}
360 
361 	if (gtk_text_iter_in_range (&end, &sr_start_iter, &sr_end_iter) &&
362 	    !gtk_text_iter_equal (&end, &sr_end_iter)) {
363 		/* ending point is inside, move the start mark */
364 		gtk_text_buffer_move_mark (region->buffer, sr->start, &end);
365 	} else {
366 		end_is_outside = TRUE;
367 		DEBUG (g_message ("end is outside"));
368 
369 	}
370 
371 	/* finally remove any intermediate subregions */
372 	done = FALSE;
373 	node = start_node;
374 
375 	while (!done) {
376 		if (node == end_node)
377 			/* we are done, exit in the next iteration */
378 			done = TRUE;
379 
380 		if ((node == start_node && !start_is_outside) ||
381 		    (node == end_node && !end_is_outside)) {
382 			/* skip starting or ending node */
383 			node = node->next;
384 		} else {
385 			GList *l = node->next;
386 			sr = node->data;
387 			gtk_text_buffer_delete_mark (region->buffer, sr->start);
388 			gtk_text_buffer_delete_mark (region->buffer, sr->end);
389 			g_free (sr);
390 			region->subregions = g_list_delete_link (region->subregions,
391 								 node);
392 			node = l;
393 		}
394 	}
395 
396 	++region->time_stamp;
397 
398 	DEBUG (pluma_text_region_debug_print (region));
399 
400 	/* now get rid of empty subregions */
401 	pluma_text_region_clear_zero_length_subregions (region);
402 
403 	DEBUG (pluma_text_region_debug_print (region));
404 }
405 
406 gint
pluma_text_region_subregions(PlumaTextRegion * region)407 pluma_text_region_subregions (PlumaTextRegion *region)
408 {
409 	g_return_val_if_fail (region != NULL, 0);
410 
411 	return g_list_length (region->subregions);
412 }
413 
414 gboolean
pluma_text_region_nth_subregion(PlumaTextRegion * region,guint subregion,GtkTextIter * start,GtkTextIter * end)415 pluma_text_region_nth_subregion (PlumaTextRegion *region,
416 			       guint          subregion,
417 			       GtkTextIter   *start,
418 			       GtkTextIter   *end)
419 {
420 	Subregion *sr;
421 
422 	g_return_val_if_fail (region != NULL, FALSE);
423 
424 	sr = g_list_nth_data (region->subregions, subregion);
425 	if (sr == NULL)
426 		return FALSE;
427 
428 	if (start)
429 		gtk_text_buffer_get_iter_at_mark (region->buffer, start, sr->start);
430 	if (end)
431 		gtk_text_buffer_get_iter_at_mark (region->buffer, end, sr->end);
432 
433 	return TRUE;
434 }
435 
436 PlumaTextRegion *
pluma_text_region_intersect(PlumaTextRegion * region,const GtkTextIter * _start,const GtkTextIter * _end)437 pluma_text_region_intersect (PlumaTextRegion     *region,
438 			   const GtkTextIter *_start,
439 			   const GtkTextIter *_end)
440 {
441 	GList *start_node, *end_node, *node;
442 	GtkTextIter sr_start_iter, sr_end_iter;
443 	Subregion *sr, *new_sr;
444 	gboolean done;
445 	PlumaTextRegion *new_region;
446 	GtkTextIter start, end;
447 
448 	g_return_val_if_fail (region != NULL && _start != NULL && _end != NULL, NULL);
449 
450 	start = *_start;
451 	end = *_end;
452 
453 	gtk_text_iter_order (&start, &end);
454 
455 	/* find bounding subregions */
456 	start_node = find_nearest_subregion (region, &start, NULL, FALSE, FALSE);
457 	end_node = find_nearest_subregion (region, &end, start_node, TRUE, FALSE);
458 
459 	/* easy case first */
460 	if (start_node == NULL || end_node == NULL || end_node == start_node->prev)
461 		return NULL;
462 
463 	new_region = pluma_text_region_new (region->buffer);
464 	done = FALSE;
465 
466 	sr = start_node->data;
467 	gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_start_iter, sr->start);
468 	gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_end_iter, sr->end);
469 
470 	/* starting node */
471 	if (gtk_text_iter_in_range (&start, &sr_start_iter, &sr_end_iter)) {
472 		new_sr = g_new0 (Subregion, 1);
473 		new_region->subregions = g_list_prepend (new_region->subregions, new_sr);
474 
475 		new_sr->start = gtk_text_buffer_create_mark (new_region->buffer, NULL,
476 							     &start, TRUE);
477 		if (start_node == end_node) {
478 			/* things will finish shortly */
479 			done = TRUE;
480 			if (gtk_text_iter_in_range (&end, &sr_start_iter, &sr_end_iter))
481 				new_sr->end = gtk_text_buffer_create_mark (new_region->buffer,
482 									   NULL, &end, FALSE);
483 			else
484 				new_sr->end = gtk_text_buffer_create_mark (new_region->buffer,
485 									   NULL, &sr_end_iter,
486 									   FALSE);
487 		} else {
488 			new_sr->end = gtk_text_buffer_create_mark (new_region->buffer, NULL,
489 								   &sr_end_iter, FALSE);
490 		}
491 		node = start_node->next;
492 	} else {
493 		/* start should be the same as the subregion, so copy it in the loop */
494 		node = start_node;
495 	}
496 
497 	if (!done) {
498 		while (node != end_node) {
499 			/* copy intermediate subregions verbatim */
500 			sr = node->data;
501 			gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_start_iter,
502 							  sr->start);
503 			gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_end_iter, sr->end);
504 
505 			new_sr = g_new0 (Subregion, 1);
506 			new_region->subregions = g_list_prepend (new_region->subregions, new_sr);
507 			new_sr->start = gtk_text_buffer_create_mark (new_region->buffer, NULL,
508 								     &sr_start_iter, TRUE);
509 			new_sr->end = gtk_text_buffer_create_mark (new_region->buffer, NULL,
510 								   &sr_end_iter, FALSE);
511 			/* next node */
512 			node = node->next;
513 		}
514 
515 		/* ending node */
516 		sr = node->data;
517 		gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_start_iter, sr->start);
518 		gtk_text_buffer_get_iter_at_mark (region->buffer, &sr_end_iter, sr->end);
519 
520 		new_sr = g_new0 (Subregion, 1);
521 		new_region->subregions = g_list_prepend (new_region->subregions, new_sr);
522 
523 		new_sr->start = gtk_text_buffer_create_mark (new_region->buffer, NULL,
524 							     &sr_start_iter, TRUE);
525 
526 		if (gtk_text_iter_in_range (&end, &sr_start_iter, &sr_end_iter))
527 			new_sr->end = gtk_text_buffer_create_mark (new_region->buffer, NULL,
528 								   &end, FALSE);
529 		else
530 			new_sr->end = gtk_text_buffer_create_mark (new_region->buffer, NULL,
531 								   &sr_end_iter, FALSE);
532 	}
533 
534 	new_region->subregions = g_list_reverse (new_region->subregions);
535 	return new_region;
536 }
537 
538 static gboolean
check_iterator(PlumaTextRegionIteratorReal * real)539 check_iterator (PlumaTextRegionIteratorReal *real)
540 {
541 	if ((real->region == NULL) ||
542 	    (real->region_time_stamp != real->region->time_stamp))
543 	{
544 		g_warning("Invalid iterator: either the iterator "
545                 	  "is uninitialized, or the region "
546                  	  "has been modified since the iterator "
547                  	  "was created.");
548 
549                 return FALSE;
550 	}
551 
552 	return TRUE;
553 }
554 
555 void
pluma_text_region_get_iterator(PlumaTextRegion * region,PlumaTextRegionIterator * iter,guint start)556 pluma_text_region_get_iterator (PlumaTextRegion         *region,
557                               PlumaTextRegionIterator *iter,
558                               guint                  start)
559 {
560 	PlumaTextRegionIteratorReal *real;
561 
562 	g_return_if_fail (region != NULL);
563 	g_return_if_fail (iter != NULL);
564 
565 	real = (PlumaTextRegionIteratorReal *)iter;
566 
567 	/* region->subregions may be NULL, -> end iter */
568 
569 	real->region = region;
570 	real->subregions = g_list_nth (region->subregions, start);
571 	real->region_time_stamp = region->time_stamp;
572 }
573 
574 gboolean
pluma_text_region_iterator_is_end(PlumaTextRegionIterator * iter)575 pluma_text_region_iterator_is_end (PlumaTextRegionIterator *iter)
576 {
577 	PlumaTextRegionIteratorReal *real;
578 
579 	g_return_val_if_fail (iter != NULL, FALSE);
580 
581 	real = (PlumaTextRegionIteratorReal *)iter;
582 	g_return_val_if_fail (check_iterator (real), FALSE);
583 
584 	return (real->subregions == NULL);
585 }
586 
587 gboolean
pluma_text_region_iterator_next(PlumaTextRegionIterator * iter)588 pluma_text_region_iterator_next (PlumaTextRegionIterator *iter)
589 {
590 	PlumaTextRegionIteratorReal *real;
591 
592 	g_return_val_if_fail (iter != NULL, FALSE);
593 
594 	real = (PlumaTextRegionIteratorReal *)iter;
595 	g_return_val_if_fail (check_iterator (real), FALSE);
596 
597 	if (real->subregions != NULL) {
598 		real->subregions = g_list_next (real->subregions);
599 		return TRUE;
600 	}
601 	else
602 		return FALSE;
603 }
604 
605 void
pluma_text_region_iterator_get_subregion(PlumaTextRegionIterator * iter,GtkTextIter * start,GtkTextIter * end)606 pluma_text_region_iterator_get_subregion (PlumaTextRegionIterator *iter,
607 					GtkTextIter           *start,
608 					GtkTextIter           *end)
609 {
610 	PlumaTextRegionIteratorReal *real;
611 	Subregion *sr;
612 
613 	g_return_if_fail (iter != NULL);
614 
615 	real = (PlumaTextRegionIteratorReal *)iter;
616 	g_return_if_fail (check_iterator (real));
617 	g_return_if_fail (real->subregions != NULL);
618 
619 	sr = (Subregion*)real->subregions->data;
620 	g_return_if_fail (sr != NULL);
621 
622 	if (start)
623 		gtk_text_buffer_get_iter_at_mark (real->region->buffer, start, sr->start);
624 	if (end)
625 		gtk_text_buffer_get_iter_at_mark (real->region->buffer, end, sr->end);
626 }
627 
628 void
pluma_text_region_debug_print(PlumaTextRegion * region)629 pluma_text_region_debug_print (PlumaTextRegion *region)
630 {
631 	GList *l;
632 
633 	g_return_if_fail (region != NULL);
634 
635 	g_print ("Subregions: ");
636 	l = region->subregions;
637 	while (l) {
638 		Subregion *sr = l->data;
639 		GtkTextIter iter1, iter2;
640 		gtk_text_buffer_get_iter_at_mark (region->buffer, &iter1, sr->start);
641 		gtk_text_buffer_get_iter_at_mark (region->buffer, &iter2, sr->end);
642 		g_print ("%d-%d ", gtk_text_iter_get_offset (&iter1),
643 			 gtk_text_iter_get_offset (&iter2));
644 		l = l->next;
645 	}
646 	g_print ("\n");
647 }
648