Blame gtksourceview/gtksourceiter.c

Packit a7d494
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- */
Packit a7d494
/* gtksourceiter.c
Packit a7d494
 * This file is part of GtkSourceView
Packit a7d494
 *
Packit a7d494
 * Copyright (C) 2014, 2016 - Sébastien Wilmet <swilmet@gnome.org>
Packit a7d494
 *
Packit a7d494
 * GtkSourceView is free software; you can redistribute it and/or
Packit a7d494
 * modify it under the terms of the GNU Lesser General Public
Packit a7d494
 * License as published by the Free Software Foundation; either
Packit a7d494
 * version 2.1 of the License, or (at your option) any later version.
Packit a7d494
 *
Packit a7d494
 * GtkSourceView is distributed in the hope that it will be useful,
Packit a7d494
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit a7d494
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit a7d494
 * Lesser General Public License for more details.
Packit a7d494
 *
Packit a7d494
 * You should have received a copy of the GNU Lesser General Public
Packit a7d494
 * License along with this library; if not, write to the Free Software
Packit a7d494
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
Packit a7d494
 */
Packit a7d494
Packit a7d494
#include "gtksourceiter.h"
Packit a7d494
Packit a7d494
/* GtkTextIter functions. Contains forward/backward functions for word
Packit a7d494
 * movements, with custom word boundaries that are used for word selection
Packit a7d494
 * (double-click) and cursor movements (Ctrl+left, Ctrl+right, etc).  The
Packit a7d494
 * initial idea was to use those word boundaries directly in GTK+, for all text
Packit a7d494
 * widgets. But in the end only the GtkTextView::extend-selection signal has
Packit a7d494
 * been added to be able to customize the boundaries for double- and
Packit a7d494
 * triple-click (the ::move-cursor and ::delete-from-cursor signals were already
Packit a7d494
 * present to customize boundaries for cursor movements). The GTK+ developers
Packit a7d494
 * didn't want to change the word boundaries for text widgets. More information:
Packit a7d494
 * https://mail.gnome.org/archives/gtk-devel-list/2014-September/msg00019.html
Packit a7d494
 * https://bugzilla.gnome.org/show_bug.cgi?id=111503
Packit a7d494
 */
Packit a7d494
Packit a7d494
/* Go to the end of the next or current "full word". A full word is a group of
Packit a7d494
 * non-blank chars.
Packit a7d494
 * In other words, this function is the same as the 'E' Vim command.
Packit a7d494
 *
Packit a7d494
 * Examples ('|' is the iter position):
Packit a7d494
 * "|---- abcd"   -> "----| abcd"
Packit a7d494
 * "|  ---- abcd" -> "  ----| abcd"
Packit a7d494
 * "--|-- abcd"   -> "----| abcd"
Packit a7d494
 * "---- a|bcd"   -> "---- abcd|"
Packit a7d494
 */
Packit a7d494
void
Packit a7d494
_gtk_source_iter_forward_full_word_end (GtkTextIter *iter)
Packit a7d494
{
Packit a7d494
	GtkTextIter pos;
Packit a7d494
	gboolean non_blank_found = FALSE;
Packit a7d494
Packit a7d494
	/* It would be better to use gtk_text_iter_forward_visible_char(), but
Packit a7d494
	 * it doesn't exist. So move by cursor position instead, it should be
Packit a7d494
	 * equivalent here.
Packit a7d494
	 */
Packit a7d494
Packit a7d494
	pos = *iter;
Packit a7d494
Packit a7d494
	while (g_unichar_isspace (gtk_text_iter_get_char (&pos)))
Packit a7d494
	{
Packit a7d494
		gtk_text_iter_forward_visible_cursor_position (&pos;;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	while (!gtk_text_iter_is_end (&pos) &&
Packit a7d494
	       !g_unichar_isspace (gtk_text_iter_get_char (&pos)))
Packit a7d494
	{
Packit a7d494
		non_blank_found = TRUE;
Packit a7d494
		gtk_text_iter_forward_visible_cursor_position (&pos;;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (non_blank_found)
Packit a7d494
	{
Packit a7d494
		*iter = pos;
Packit a7d494
	}
Packit a7d494
}
Packit a7d494
Packit a7d494
/* Symmetric of iter_forward_full_word_end(). */
Packit a7d494
void
Packit a7d494
_gtk_source_iter_backward_full_word_start (GtkTextIter *iter)
Packit a7d494
{
Packit a7d494
	GtkTextIter pos;
Packit a7d494
	GtkTextIter prev;
Packit a7d494
	gboolean non_blank_found = FALSE;
Packit a7d494
Packit a7d494
	pos = *iter;
Packit a7d494
Packit a7d494
	while (!gtk_text_iter_is_start (&pos))
Packit a7d494
	{
Packit a7d494
		prev = pos;
Packit a7d494
		gtk_text_iter_backward_visible_cursor_position (&prev;;
Packit a7d494
Packit a7d494
		if (!g_unichar_isspace (gtk_text_iter_get_char (&prev)))
Packit a7d494
		{
Packit a7d494
			break;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		pos = prev;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	while (!gtk_text_iter_is_start (&pos))
Packit a7d494
	{
Packit a7d494
		prev = pos;
Packit a7d494
		gtk_text_iter_backward_visible_cursor_position (&prev;;
Packit a7d494
Packit a7d494
		if (g_unichar_isspace (gtk_text_iter_get_char (&prev)))
Packit a7d494
		{
Packit a7d494
			break;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		non_blank_found = TRUE;
Packit a7d494
		pos = prev;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (non_blank_found)
Packit a7d494
	{
Packit a7d494
		*iter = pos;
Packit a7d494
	}
Packit a7d494
}
Packit a7d494
Packit a7d494
gboolean
Packit a7d494
_gtk_source_iter_starts_full_word (const GtkTextIter *iter)
Packit a7d494
{
Packit a7d494
	GtkTextIter prev = *iter;
Packit a7d494
Packit a7d494
	if (gtk_text_iter_is_end (iter))
Packit a7d494
	{
Packit a7d494
		return FALSE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (!gtk_text_iter_backward_visible_cursor_position (&prev))
Packit a7d494
	{
Packit a7d494
		return !g_unichar_isspace (gtk_text_iter_get_char (iter));
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return (g_unichar_isspace (gtk_text_iter_get_char (&prev)) &&
Packit a7d494
		!g_unichar_isspace (gtk_text_iter_get_char (iter)));
Packit a7d494
}
Packit a7d494
Packit a7d494
gboolean
Packit a7d494
_gtk_source_iter_ends_full_word (const GtkTextIter *iter)
Packit a7d494
{
Packit a7d494
	GtkTextIter prev = *iter;
Packit a7d494
Packit a7d494
	if (!gtk_text_iter_backward_visible_cursor_position (&prev))
Packit a7d494
	{
Packit a7d494
		return FALSE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return (!g_unichar_isspace (gtk_text_iter_get_char (&prev)) &&
Packit a7d494
		(gtk_text_iter_is_end (iter) ||
Packit a7d494
		 g_unichar_isspace (gtk_text_iter_get_char (iter))));
Packit a7d494
}
Packit a7d494
Packit a7d494
/* Extends the definition of a natural-language word used by Pango. The
Packit a7d494
 * underscore is added to the possible characters of a natural-language word.
Packit a7d494
 */
Packit a7d494
void
Packit a7d494
_gtk_source_iter_forward_extra_natural_word_end (GtkTextIter *iter)
Packit a7d494
{
Packit a7d494
	GtkTextIter next_word_end = *iter;
Packit a7d494
	GtkTextIter next_underscore_end = *iter;
Packit a7d494
	GtkTextIter *limit = NULL;
Packit a7d494
	gboolean found;
Packit a7d494
Packit a7d494
	if (gtk_text_iter_forward_visible_word_end (&next_word_end))
Packit a7d494
	{
Packit a7d494
		limit = &next_word_end;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	found = gtk_text_iter_forward_search (iter,
Packit a7d494
					      "_",
Packit a7d494
					      GTK_TEXT_SEARCH_VISIBLE_ONLY | GTK_TEXT_SEARCH_TEXT_ONLY,
Packit a7d494
					      NULL,
Packit a7d494
					      &next_underscore_end,
Packit a7d494
					      limit);
Packit a7d494
Packit a7d494
	if (found)
Packit a7d494
	{
Packit a7d494
		*iter = next_underscore_end;
Packit a7d494
	}
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		*iter = next_word_end;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	while (TRUE)
Packit a7d494
	{
Packit a7d494
		if (gtk_text_iter_get_char (iter) == '_')
Packit a7d494
		{
Packit a7d494
			gtk_text_iter_forward_visible_cursor_position (iter);
Packit a7d494
		}
Packit a7d494
		else if (gtk_text_iter_starts_word (iter))
Packit a7d494
		{
Packit a7d494
			gtk_text_iter_forward_visible_word_end (iter);
Packit a7d494
		}
Packit a7d494
		else
Packit a7d494
		{
Packit a7d494
			break;
Packit a7d494
		}
Packit a7d494
	}
Packit a7d494
}
Packit a7d494
Packit a7d494
/* Symmetric of iter_forward_extra_natural_word_end(). */
Packit a7d494
void
Packit a7d494
_gtk_source_iter_backward_extra_natural_word_start (GtkTextIter *iter)
Packit a7d494
{
Packit a7d494
	GtkTextIter prev_word_start = *iter;
Packit a7d494
	GtkTextIter prev_underscore_start = *iter;
Packit a7d494
	GtkTextIter *limit = NULL;
Packit a7d494
	gboolean found;
Packit a7d494
Packit a7d494
	if (gtk_text_iter_backward_visible_word_start (&prev_word_start))
Packit a7d494
	{
Packit a7d494
		limit = &prev_word_start;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	found = gtk_text_iter_backward_search (iter,
Packit a7d494
					       "_",
Packit a7d494
					       GTK_TEXT_SEARCH_VISIBLE_ONLY | GTK_TEXT_SEARCH_TEXT_ONLY,
Packit a7d494
					       &prev_underscore_start,
Packit a7d494
					       NULL,
Packit a7d494
					       limit);
Packit a7d494
Packit a7d494
	if (found)
Packit a7d494
	{
Packit a7d494
		*iter = prev_underscore_start;
Packit a7d494
	}
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		*iter = prev_word_start;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	while (!gtk_text_iter_is_start (iter))
Packit a7d494
	{
Packit a7d494
		GtkTextIter prev = *iter;
Packit a7d494
		gtk_text_iter_backward_visible_cursor_position (&prev;;
Packit a7d494
Packit a7d494
		if (gtk_text_iter_get_char (&prev) == '_')
Packit a7d494
		{
Packit a7d494
			*iter = prev;
Packit a7d494
		}
Packit a7d494
		else if (gtk_text_iter_ends_word (iter))
Packit a7d494
		{
Packit a7d494
			gtk_text_iter_backward_visible_word_start (iter);
Packit a7d494
		}
Packit a7d494
		else
Packit a7d494
		{
Packit a7d494
			break;
Packit a7d494
		}
Packit a7d494
	}
Packit a7d494
}
Packit a7d494
Packit a7d494
static gboolean
Packit a7d494
backward_cursor_position (GtkTextIter *iter,
Packit a7d494
			  gboolean     visible)
Packit a7d494
{
Packit a7d494
	if (visible)
Packit a7d494
	{
Packit a7d494
		return gtk_text_iter_backward_visible_cursor_position (iter);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return gtk_text_iter_backward_cursor_position (iter);
Packit a7d494
}
Packit a7d494
Packit a7d494
gboolean
Packit a7d494
_gtk_source_iter_starts_extra_natural_word (const GtkTextIter *iter,
Packit a7d494
					    gboolean           visible)
Packit a7d494
{
Packit a7d494
	gboolean starts_word;
Packit a7d494
	GtkTextIter prev;
Packit a7d494
Packit a7d494
	starts_word = gtk_text_iter_starts_word (iter);
Packit a7d494
Packit a7d494
	prev = *iter;
Packit a7d494
	if (!backward_cursor_position (&prev, visible))
Packit a7d494
	{
Packit a7d494
		return starts_word || gtk_text_iter_get_char (iter) == '_';
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (starts_word)
Packit a7d494
	{
Packit a7d494
		return gtk_text_iter_get_char (&prev) != '_';
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return (gtk_text_iter_get_char (iter) == '_' &&
Packit a7d494
		gtk_text_iter_get_char (&prev) != '_' &&
Packit a7d494
		!gtk_text_iter_ends_word (iter));
Packit a7d494
}
Packit a7d494
Packit a7d494
gboolean
Packit a7d494
_gtk_source_iter_ends_extra_natural_word (const GtkTextIter *iter,
Packit a7d494
					  gboolean           visible)
Packit a7d494
{
Packit a7d494
	GtkTextIter prev;
Packit a7d494
	gboolean ends_word;
Packit a7d494
Packit a7d494
	prev = *iter;
Packit a7d494
	if (!backward_cursor_position (&prev, visible))
Packit a7d494
	{
Packit a7d494
		return FALSE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	ends_word = gtk_text_iter_ends_word (iter);
Packit a7d494
Packit a7d494
	if (gtk_text_iter_is_end (iter))
Packit a7d494
	{
Packit a7d494
		return ends_word || gtk_text_iter_get_char (&prev) == '_';
Packit a7d494
	}
Packit a7d494
Packit a7d494
	if (ends_word)
Packit a7d494
	{
Packit a7d494
		return gtk_text_iter_get_char (iter) != '_';
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return (gtk_text_iter_get_char (&prev) == '_' &&
Packit a7d494
		gtk_text_iter_get_char (iter) != '_' &&
Packit a7d494
		!gtk_text_iter_starts_word (iter));
Packit a7d494
}
Packit a7d494
Packit a7d494
/* Similar to gtk_text_iter_forward_visible_word_end, but with a custom
Packit a7d494
 * definition of "word".
Packit a7d494
 *
Packit a7d494
 * It is normally the same word boundaries as in Vim. This function is the same
Packit a7d494
 * as the 'e' command.
Packit a7d494
 *
Packit a7d494
 * With the custom word definition, a word can be:
Packit a7d494
 * - a natural-language word as defined by Pango, plus the underscore. The
Packit a7d494
 *   underscore is added because it is often used in programming languages.
Packit a7d494
 * - a group of contiguous non-blank characters.
Packit a7d494
 */
Packit a7d494
gboolean
Packit a7d494
_gtk_source_iter_forward_visible_word_end (GtkTextIter *iter)
Packit a7d494
{
Packit a7d494
	GtkTextIter orig = *iter;
Packit a7d494
	GtkTextIter farthest = *iter;
Packit a7d494
	GtkTextIter next_word_end = *iter;
Packit a7d494
	GtkTextIter word_start;
Packit a7d494
Packit a7d494
	/* 'farthest' is the farthest position that this function can return. Example:
Packit a7d494
	 * "|---- aaaa"  ->  "----| aaaa"
Packit a7d494
	 */
Packit a7d494
	_gtk_source_iter_forward_full_word_end (&farthest);
Packit a7d494
Packit a7d494
	/* Go to the next extra-natural word end. It can be farther than
Packit a7d494
	 * 'farthest':
Packit a7d494
	 * "|---- aaaa"  ->  "---- aaaa|"
Packit a7d494
	 *
Packit a7d494
	 * Or it can remain at the same place:
Packit a7d494
	 * "aaaa| ----"  ->  "aaaa| ----"
Packit a7d494
	 */
Packit a7d494
	_gtk_source_iter_forward_extra_natural_word_end (&next_word_end);
Packit a7d494
Packit a7d494
	if (gtk_text_iter_compare (&farthest, &next_word_end) < 0 ||
Packit a7d494
	    gtk_text_iter_equal (iter, &next_word_end))
Packit a7d494
	{
Packit a7d494
		*iter = farthest;
Packit a7d494
		goto end;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	/* From 'next_word_end', go to the previous extra-natural word start.
Packit a7d494
	 *
Packit a7d494
	 * Example 1:
Packit a7d494
	 * iter:          "ab|cd"
Packit a7d494
	 * next_word_end: "abcd|" -> the good one
Packit a7d494
	 * word_start:    "|abcd"
Packit a7d494
	 *
Packit a7d494
	 * Example 2:
Packit a7d494
	 * iter:          "| abcd()"
Packit a7d494
	 * next_word_end: " abcd|()" -> the good one
Packit a7d494
	 * word_start:    " |abcd()"
Packit a7d494
	 *
Packit a7d494
	 * Example 3:
Packit a7d494
	 * iter:          "abcd|()efgh"
Packit a7d494
	 * next_word_end: "abcd()efgh|"
Packit a7d494
	 * word_start:    "abcd()|efgh" -> the good one, at the end of the word "()".
Packit a7d494
	 */
Packit a7d494
	word_start = next_word_end;
Packit a7d494
	_gtk_source_iter_backward_extra_natural_word_start (&word_start);
Packit a7d494
Packit a7d494
	/* Example 1 */
Packit a7d494
	if (gtk_text_iter_compare (&word_start, iter) <= 0)
Packit a7d494
	{
Packit a7d494
		*iter = next_word_end;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	/* Example 2 */
Packit a7d494
	else if (_gtk_source_iter_starts_full_word (&word_start))
Packit a7d494
	{
Packit a7d494
		*iter = next_word_end;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	/* Example 3 */
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		*iter = word_start;
Packit a7d494
	}
Packit a7d494
Packit a7d494
end:
Packit a7d494
	return !gtk_text_iter_equal (&orig, iter) && !gtk_text_iter_is_end (iter);
Packit a7d494
}
Packit a7d494
Packit a7d494
/* Symmetric of _gtk_source_iter_forward_visible_word_end(). */
Packit a7d494
gboolean
Packit a7d494
_gtk_source_iter_backward_visible_word_start (GtkTextIter *iter)
Packit a7d494
{
Packit a7d494
	GtkTextIter orig = *iter;
Packit a7d494
	GtkTextIter farthest = *iter;
Packit a7d494
	GtkTextIter prev_word_start = *iter;
Packit a7d494
	GtkTextIter word_end;
Packit a7d494
Packit a7d494
	/* 'farthest' is the farthest position that this function can return. Example:
Packit a7d494
	 * "aaaa ----|"  ->  "aaaa |----"
Packit a7d494
	 */
Packit a7d494
	_gtk_source_iter_backward_full_word_start (&farthest);
Packit a7d494
Packit a7d494
	/* Go to the previous extra-natural word start. It can be farther than
Packit a7d494
	 * 'farthest':
Packit a7d494
	 * "aaaa ----|"  ->  "|aaaa ----"
Packit a7d494
	 *
Packit a7d494
	 * Or it can remain at the same place:
Packit a7d494
	 * "---- |aaaa"  ->  "---- |aaaa"
Packit a7d494
	 */
Packit a7d494
	_gtk_source_iter_backward_extra_natural_word_start (&prev_word_start);
Packit a7d494
Packit a7d494
	if (gtk_text_iter_compare (&prev_word_start, &farthest) < 0 ||
Packit a7d494
	    gtk_text_iter_equal (iter, &prev_word_start))
Packit a7d494
	{
Packit a7d494
		*iter = farthest;
Packit a7d494
		goto end;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	/* From 'prev_word_start', go to the next extra-natural word end.
Packit a7d494
	 *
Packit a7d494
	 * Example 1:
Packit a7d494
	 * iter:            "ab|cd"
Packit a7d494
	 * prev_word_start: "|abcd" -> the good one
Packit a7d494
	 * word_end:        "abcd|"
Packit a7d494
	 *
Packit a7d494
	 * Example 2:
Packit a7d494
	 * iter:            "()abcd |"
Packit a7d494
	 * prev_word_start: "()|abcd " -> the good one
Packit a7d494
	 * word_end:        "()abcd| "
Packit a7d494
	 *
Packit a7d494
	 * Example 3:
Packit a7d494
	 * iter:            "abcd()|"
Packit a7d494
	 * prev_word_start: "|abcd()"
Packit a7d494
	 * word_end:        "abcd|()" -> the good one, at the start of the word "()".
Packit a7d494
	 */
Packit a7d494
	word_end = prev_word_start;
Packit a7d494
	_gtk_source_iter_forward_extra_natural_word_end (&word_end);
Packit a7d494
Packit a7d494
	/* Example 1 */
Packit a7d494
	if (gtk_text_iter_compare (iter, &word_end) <= 0)
Packit a7d494
	{
Packit a7d494
		*iter = prev_word_start;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	/* Example 2 */
Packit a7d494
	else if (_gtk_source_iter_ends_full_word (&word_end))
Packit a7d494
	{
Packit a7d494
		*iter = prev_word_start;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	/* Example 3 */
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		*iter = word_end;
Packit a7d494
	}
Packit a7d494
Packit a7d494
end:
Packit a7d494
	return !gtk_text_iter_equal (&orig, iter) && !gtk_text_iter_is_end (iter);
Packit a7d494
}
Packit a7d494
Packit a7d494
/* Similar to gtk_text_iter_forward_visible_word_ends(). */
Packit a7d494
gboolean
Packit a7d494
_gtk_source_iter_forward_visible_word_ends (GtkTextIter *iter,
Packit a7d494
					    gint         count)
Packit a7d494
{
Packit a7d494
	GtkTextIter orig = *iter;
Packit a7d494
	gint i;
Packit a7d494
Packit a7d494
	if (count < 0)
Packit a7d494
	{
Packit a7d494
		return _gtk_source_iter_backward_visible_word_starts (iter, -count);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	for (i = 0; i < count; i++)
Packit a7d494
	{
Packit a7d494
		if (!_gtk_source_iter_forward_visible_word_end (iter))
Packit a7d494
		{
Packit a7d494
			break;
Packit a7d494
		}
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return !gtk_text_iter_equal (&orig, iter) && !gtk_text_iter_is_end (iter);
Packit a7d494
}
Packit a7d494
Packit a7d494
/* Similar to gtk_text_iter_backward_visible_word_starts(). */
Packit a7d494
gboolean
Packit a7d494
_gtk_source_iter_backward_visible_word_starts (GtkTextIter *iter,
Packit a7d494
					       gint         count)
Packit a7d494
{
Packit a7d494
	GtkTextIter orig = *iter;
Packit a7d494
	gint i;
Packit a7d494
Packit a7d494
	if (count < 0)
Packit a7d494
	{
Packit a7d494
		return _gtk_source_iter_forward_visible_word_ends (iter, -count);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	for (i = 0; i < count; i++)
Packit a7d494
	{
Packit a7d494
		if (!_gtk_source_iter_backward_visible_word_start (iter))
Packit a7d494
		{
Packit a7d494
			break;
Packit a7d494
		}
Packit a7d494
	}
Packit a7d494
Packit a7d494
	return !gtk_text_iter_equal (&orig, iter) && !gtk_text_iter_is_end (iter);
Packit a7d494
}
Packit a7d494
Packit a7d494
gboolean
Packit a7d494
_gtk_source_iter_starts_word (const GtkTextIter *iter)
Packit a7d494
{
Packit a7d494
	if (_gtk_source_iter_starts_full_word (iter) ||
Packit a7d494
	    _gtk_source_iter_starts_extra_natural_word (iter, TRUE))
Packit a7d494
	{
Packit a7d494
		return TRUE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	/* Example: "abcd|()", at the start of the word "()". */
Packit a7d494
	return (!_gtk_source_iter_ends_full_word (iter) &&
Packit a7d494
		_gtk_source_iter_ends_extra_natural_word (iter, TRUE));
Packit a7d494
}
Packit a7d494
Packit a7d494
gboolean
Packit a7d494
_gtk_source_iter_ends_word (const GtkTextIter *iter)
Packit a7d494
{
Packit a7d494
	if (_gtk_source_iter_ends_full_word (iter) ||
Packit a7d494
	    _gtk_source_iter_ends_extra_natural_word (iter, TRUE))
Packit a7d494
	{
Packit a7d494
		return TRUE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	/* Example: "abcd()|efgh", at the end of the word "()". */
Packit a7d494
	return (!_gtk_source_iter_starts_full_word (iter) &&
Packit a7d494
		_gtk_source_iter_starts_extra_natural_word (iter, TRUE));
Packit a7d494
}
Packit a7d494
Packit a7d494
gboolean
Packit a7d494
_gtk_source_iter_inside_word (const GtkTextIter *iter)
Packit a7d494
{
Packit a7d494
	GtkTextIter prev_word_start;
Packit a7d494
	GtkTextIter word_end;
Packit a7d494
Packit a7d494
	if (_gtk_source_iter_starts_word (iter))
Packit a7d494
	{
Packit a7d494
		return TRUE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	prev_word_start = *iter;
Packit a7d494
	if (!_gtk_source_iter_backward_visible_word_start (&prev_word_start))
Packit a7d494
	{
Packit a7d494
		return FALSE;
Packit a7d494
	}
Packit a7d494
Packit a7d494
	word_end = prev_word_start;
Packit a7d494
	_gtk_source_iter_forward_visible_word_end (&word_end);
Packit a7d494
Packit a7d494
	return (gtk_text_iter_compare (&prev_word_start, iter) <= 0 &&
Packit a7d494
		gtk_text_iter_compare (iter, &word_end) < 0);
Packit a7d494
}
Packit a7d494
Packit a7d494
/* Used for the GtkTextView::extend-selection signal. */
Packit a7d494
void
Packit a7d494
_gtk_source_iter_extend_selection_word (const GtkTextIter *location,
Packit a7d494
					GtkTextIter       *start,
Packit a7d494
					GtkTextIter       *end)
Packit a7d494
{
Packit a7d494
	/* Exactly the same algorithm as in GTK+, but with our custom word
Packit a7d494
	 * boundaries.
Packit a7d494
	 */
Packit a7d494
	*start = *location;
Packit a7d494
	*end = *location;
Packit a7d494
Packit a7d494
	if (_gtk_source_iter_inside_word (start))
Packit a7d494
	{
Packit a7d494
		if (!_gtk_source_iter_starts_word (start))
Packit a7d494
		{
Packit a7d494
			_gtk_source_iter_backward_visible_word_start (start);
Packit a7d494
		}
Packit a7d494
Packit a7d494
		if (!_gtk_source_iter_ends_word (end))
Packit a7d494
		{
Packit a7d494
			_gtk_source_iter_forward_visible_word_end (end);
Packit a7d494
		}
Packit a7d494
	}
Packit a7d494
	else
Packit a7d494
	{
Packit a7d494
		GtkTextIter tmp;
Packit a7d494
Packit a7d494
		tmp = *start;
Packit a7d494
		if (_gtk_source_iter_backward_visible_word_start (&tmp))
Packit a7d494
		{
Packit a7d494
			_gtk_source_iter_forward_visible_word_end (&tmp);
Packit a7d494
		}
Packit a7d494
Packit a7d494
		if (gtk_text_iter_get_line (&tmp) == gtk_text_iter_get_line (start))
Packit a7d494
		{
Packit a7d494
			*start = tmp;
Packit a7d494
		}
Packit a7d494
		else
Packit a7d494
		{
Packit a7d494
			gtk_text_iter_set_line_offset (start, 0);
Packit a7d494
		}
Packit a7d494
Packit a7d494
		tmp = *end;
Packit a7d494
		if (!_gtk_source_iter_forward_visible_word_end (&tmp))
Packit a7d494
		{
Packit a7d494
			gtk_text_iter_forward_to_end (&tmp);
Packit a7d494
		}
Packit a7d494
Packit a7d494
		if (_gtk_source_iter_ends_word (&tmp))
Packit a7d494
		{
Packit a7d494
			_gtk_source_iter_backward_visible_word_start (&tmp);
Packit a7d494
		}
Packit a7d494
Packit a7d494
		if (gtk_text_iter_get_line (&tmp) == gtk_text_iter_get_line (end))
Packit a7d494
		{
Packit a7d494
			*end = tmp;
Packit a7d494
		}
Packit a7d494
		else
Packit a7d494
		{
Packit a7d494
			gtk_text_iter_forward_to_line_end (end);
Packit a7d494
		}
Packit a7d494
	}
Packit a7d494
}
Packit a7d494
Packit a7d494
/* Get the boundary, on @iter's line, between leading spaces (indentation) and
Packit a7d494
 * the text.
Packit a7d494
 */
Packit a7d494
void
Packit a7d494
_gtk_source_iter_get_leading_spaces_end_boundary (const GtkTextIter *iter,
Packit a7d494
						  GtkTextIter       *leading_end)
Packit a7d494
{
Packit a7d494
	g_return_if_fail (iter != NULL);
Packit a7d494
	g_return_if_fail (leading_end != NULL);
Packit a7d494
Packit a7d494
	*leading_end = *iter;
Packit a7d494
	gtk_text_iter_set_line_offset (leading_end, 0);
Packit a7d494
Packit a7d494
	while (!gtk_text_iter_ends_line (leading_end))
Packit a7d494
	{
Packit a7d494
		gunichar ch = gtk_text_iter_get_char (leading_end);
Packit a7d494
Packit a7d494
		if (!g_unichar_isspace (ch))
Packit a7d494
		{
Packit a7d494
			break;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		gtk_text_iter_forward_char (leading_end);
Packit a7d494
	}
Packit a7d494
}
Packit a7d494
Packit a7d494
/* Get the boundary, on @iter's line, between the end of the text and trailing
Packit a7d494
 * spaces.
Packit a7d494
 */
Packit a7d494
void
Packit a7d494
_gtk_source_iter_get_trailing_spaces_start_boundary (const GtkTextIter *iter,
Packit a7d494
						     GtkTextIter       *trailing_start)
Packit a7d494
{
Packit a7d494
	g_return_if_fail (iter != NULL);
Packit a7d494
	g_return_if_fail (trailing_start != NULL);
Packit a7d494
Packit a7d494
	*trailing_start = *iter;
Packit a7d494
	if (!gtk_text_iter_ends_line (trailing_start))
Packit a7d494
	{
Packit a7d494
		gtk_text_iter_forward_to_line_end (trailing_start);
Packit a7d494
	}
Packit a7d494
Packit a7d494
	while (!gtk_text_iter_starts_line (trailing_start))
Packit a7d494
	{
Packit a7d494
		GtkTextIter prev;
Packit a7d494
		gunichar ch;
Packit a7d494
Packit a7d494
		prev = *trailing_start;
Packit a7d494
		gtk_text_iter_backward_char (&prev;;
Packit a7d494
Packit a7d494
		ch = gtk_text_iter_get_char (&prev;;
Packit a7d494
		if (!g_unichar_isspace (ch))
Packit a7d494
		{
Packit a7d494
			break;
Packit a7d494
		}
Packit a7d494
Packit a7d494
		*trailing_start = prev;
Packit a7d494
	}
Packit a7d494
}