[gtksourceview/wip/chergert/snippets] more work on switching to marks
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtksourceview/wip/chergert/snippets] more work on switching to marks
- Date: Thu, 23 Jan 2020 20:09:48 +0000 (UTC)
commit cb9c4d6e4b3927196bbca8b37152c2c453d382c8
Author: Christian Hergert <chergert redhat com>
Date: Thu Jan 23 12:09:48 2020 -0800
more work on switching to marks
gtksourceview/gtksourcesnippet.c | 378 ++++++++++++++++++--------
gtksourceview/gtksourcesnippetchunk-private.h | 2 +-
gtksourceview/gtksourcesnippetchunk.c | 33 ++-
3 files changed, 288 insertions(+), 125 deletions(-)
---
diff --git a/gtksourceview/gtksourcesnippet.c b/gtksourceview/gtksourcesnippet.c
index ae190faa..c8692791 100644
--- a/gtksourceview/gtksourcesnippet.c
+++ b/gtksourceview/gtksourcesnippet.c
@@ -41,6 +41,14 @@ struct _GtkSourceSnippet
const gchar *language_id;
gchar *description;
+ /* This is used to track the insert position within a snippet
+ * while we make transforms. We don't use marks here because
+ * the gravity of the mark is not enought o assure we end up
+ * at the correct position. So instead we are relative to the
+ * beginning of the snippet.
+ */
+ gint saved_insert_pos;
+
gint focus_position;
gint max_focus_position;
@@ -63,6 +71,29 @@ enum {
static GParamSpec *properties [N_PROPS];
+static void gtk_source_snippet_update_marks (GtkSourceSnippet *snippet);
+static void gtk_source_snippet_update_tags (GtkSourceSnippet *self);
+
+static inline void
+print_chunk_positions (GtkSourceSnippet *snippet)
+{
+ for (guint i = 0; i < snippet->chunks->len; i++)
+ {
+ GtkSourceSnippetChunk *chunk = g_ptr_array_index (snippet->chunks, i);
+ GtkTextIter begin, end;
+
+ if (_gtk_source_snippet_chunk_get_bounds (chunk, &begin, &end))
+ {
+ g_printerr ("Chunk %u: %u:%u to %u:%u\n",
+ i,
+ gtk_text_iter_get_line (&begin),
+ gtk_text_iter_get_line_offset (&begin),
+ gtk_text_iter_get_line (&end),
+ gtk_text_iter_get_line_offset (&end));
+ }
+ }
+}
+
static GtkSourceSnippetChunk *
get_chunk_at_iter (GtkSourceSnippet *self,
GtkTextIter *iter)
@@ -96,6 +127,54 @@ get_chunk_at_iter (GtkSourceSnippet *self,
g_return_val_if_reached (NULL);
}
+static void
+gtk_source_snippet_save_insert (GtkSourceSnippet *snippet)
+{
+ GtkTextMark *insert;
+ GtkTextIter iter;
+ GtkTextIter begin;
+ GtkTextIter end;
+
+ g_assert (GTK_SOURCE_IS_SNIPPET (snippet));
+
+ if (snippet->current_chunk == NULL ||
+ !_gtk_source_snippet_chunk_get_bounds (snippet->current_chunk, &begin, &end))
+ {
+ snippet->saved_insert_pos = 0;
+ return;
+ }
+
+ insert = gtk_text_buffer_get_insert (snippet->buffer);
+ gtk_text_buffer_get_iter_at_mark (snippet->buffer, &iter, insert);
+
+ if (_gtk_source_snippet_chunk_contains (snippet->current_chunk, &iter))
+ {
+ snippet->saved_insert_pos =
+ gtk_text_iter_get_offset (&iter) -
+ gtk_text_iter_get_offset (&begin);
+ }
+}
+
+static void
+gtk_source_snippet_restore_insert (GtkSourceSnippet *snippet)
+{
+ GtkTextIter begin;
+ GtkTextIter end;
+
+ g_assert (GTK_SOURCE_IS_SNIPPET (snippet));
+
+ if (snippet->current_chunk == NULL ||
+ !_gtk_source_snippet_chunk_get_bounds (snippet->current_chunk, &begin, &end))
+ {
+ snippet->saved_insert_pos = 0;
+ return;
+ }
+
+ gtk_text_iter_forward_chars (&begin, snippet->saved_insert_pos);
+ gtk_text_buffer_select_range (snippet->buffer, &begin, &begin);
+ snippet->saved_insert_pos = 0;
+}
+
/**
* gtk_source_snippet_new:
* @trigger: (nullable): the trigger word
@@ -639,6 +718,9 @@ _gtk_source_snippet_begin (GtkSourceSnippet *self,
mark = gtk_text_buffer_create_mark (buffer, NULL, iter, TRUE);
self->mark_begin = g_object_ref (mark);
+ mark = gtk_text_buffer_create_mark (buffer, NULL, iter, FALSE);
+ self->mark_end = g_object_ref (mark);
+
gtk_text_buffer_begin_user_action (buffer);
for (guint i = 0; i < self->chunks->len; i++)
@@ -652,19 +734,20 @@ _gtk_source_snippet_begin (GtkSourceSnippet *self,
text = gtk_source_snippet_chunk_get_text (chunk);
begin = gtk_text_buffer_create_mark (buffer, NULL, iter, TRUE);
+ end = gtk_text_buffer_create_mark (buffer, NULL, iter, FALSE);
+
_gtk_source_snippet_chunk_set_begin_mark (chunk, begin);
+ _gtk_source_snippet_chunk_set_end_mark (chunk, end);
- if (text != NULL)
+ if (text != NULL && text[0] != 0)
{
+ self->current_chunk = chunk;
gtk_text_buffer_insert (buffer, iter, text, -1);
+ gtk_source_snippet_update_marks (self);
}
-
- end = gtk_text_buffer_create_mark (buffer, NULL, iter, FALSE);
- _gtk_source_snippet_chunk_set_end_mark (chunk, end);
}
- mark = gtk_text_buffer_create_mark (buffer, NULL, iter, FALSE);
- self->mark_end = g_object_ref (mark);
+ self->current_chunk = NULL;
gtk_text_buffer_end_user_action (buffer);
@@ -704,48 +787,135 @@ gtk_source_snippet_add_chunk (GtkSourceSnippet *self,
}
static void
-gtk_source_snippet_replace_chunk_text (GtkSourceSnippet *self,
- GtkSourceSnippetChunk *chunk,
- const gchar *text)
+gtk_source_snippet_update_marks (GtkSourceSnippet *snippet)
{
- GtkTextIter begin;
- GtkTextIter end;
- gint diff;
+ GtkTextBuffer *buffer;
- g_assert (GTK_SOURCE_IS_SNIPPET (self));
- g_assert (GTK_SOURCE_IS_SNIPPET_CHUNK (chunk));
-
- /*
- * This replaces the text for the snippet. We insert new text before
- * we delete the old text to ensure things are more stable as we
- * manipulate the runs. Avoiding zero-length runs, even temporarily
- * can be helpful to reduce chances for textmark gravity overlapping
- * other marks.
+ g_assert (GTK_SOURCE_IS_SNIPPET (snippet));
+
+ /* If the begin of this chunk has come before the end
+ * of the last chunk, then that mights we are empty and
+ * the right gravity of the begin mark was greedily taken
+ * when inserting into a previous mark. This can happen
+ * when you (often intermittently) have empty chunks.
+ *
+ * For example, imagine 4 empty chunks:
+ *
+ * [][][][]
+ *
+ * Except in reality to GtkTextBuffer, that's more like:
+ *
+ * [[[[]]]]
+ *
+ * When the user types 't' into the first chunk we'll end up
+ * with something like this:
+ *
+ * [[[[t]]]]
+ *
+ * and we need to modify things to look like this:
+ *
+ * [t][[[]]]
+ *
+ * We also must worry about the situation where text
+ * is inserted into the second position like:
+ *
+ * [t[t]][[]]
+ *
+ * and detect the situation to move the end mark for the
+ * first item backwards into:
+ *
+ * [t][t][[]]
*/
- _gtk_source_snippet_chunk_get_bounds (chunk, &begin, &end);
+#if 1
+ g_printerr ("Before:\n");
+ print_chunk_positions (snippet);
+#endif
- gtk_text_iter_order (&begin, &end);
- diff = gtk_text_iter_get_offset (&end) - gtk_text_iter_get_offset (&begin);
+ if (snippet->chunks->len == 0)
+ {
+ return;
+ }
- if (text != NULL)
+ buffer = GTK_TEXT_BUFFER (snippet->buffer);
+
+ /* For all marks before the current_chunk we want to ensure that their
+ * end_mark has not moved past the begin_mark for the chunk after it.
+ * That can happen when text is inserted at the beginning of a chunk as
+ * the right gravity mark from the adjacent chunk will be moved.
+ */
+ for (guint i = 0; i < snippet->chunks->len - 1; i++)
{
- gtk_text_buffer_insert (self->buffer, &begin, text, -1);
+ GtkSourceSnippetChunk *chunk = g_ptr_array_index (snippet->chunks, i);
+ GtkSourceSnippetChunk *next = g_ptr_array_index (snippet->chunks, i + 1);
+ GtkTextIter begin, end;
+ GtkTextIter next_begin, next_end;
+
+ if (!_gtk_source_snippet_chunk_get_bounds (chunk, &begin, &end) ||
+ !_gtk_source_snippet_chunk_get_bounds (next, &next_begin, &next_end))
+ {
+ break;
+ }
+
+ if (gtk_text_iter_compare (&end, &next_begin) > 0)
+ {
+ gtk_text_buffer_move_mark (buffer,
+ _gtk_source_snippet_chunk_get_end_mark (chunk),
+ &next_begin);
+ }
+
+ if (chunk == snippet->current_chunk)
+ {
+ break;
+ }
}
- if (diff > 0)
+#if 1
+ g_printerr ("After move up to current_chunk:\n");
+ print_chunk_positions (snippet);
+#endif
+
+ /* Now for all of the snippets, if their start position becomes before
+ * the previous end position, move it forward. This can happen when you
+ * have adjacent chunks and the gravity keeps the mark in the wrong
+ * position relative to other marks.
+ */
+ for (guint i = 1; i < snippet->chunks->len; i++)
{
- end = begin;
- gtk_text_iter_forward_chars (&end, diff);
- gtk_text_buffer_delete (self->buffer, &begin, &end);
+ GtkSourceSnippetChunk *chunk = g_ptr_array_index (snippet->chunks, i);
+ GtkSourceSnippetChunk *prev = g_ptr_array_index (snippet->chunks, i - 1);
+ GtkTextIter begin, end;
+ GtkTextIter prev_begin, prev_end;
+
+ if (!_gtk_source_snippet_chunk_get_bounds (chunk, &begin, &end) ||
+ !_gtk_source_snippet_chunk_get_bounds (prev, &prev_begin, &prev_end))
+ {
+ break;
+ }
+
+ if (gtk_text_iter_compare (&begin, &prev_end) < 0)
+ {
+ gtk_text_buffer_move_mark (buffer,
+ _gtk_source_snippet_chunk_get_begin_mark (chunk),
+ &end);
+ }
}
+
+#if 1
+ g_printerr ("After move of all:\n");
+ print_chunk_positions (snippet);
+#endif
}
static void
gtk_source_snippet_rewrite_updated_chunks (GtkSourceSnippet *self)
{
+ GtkSourceSnippetChunk *current;
+
g_return_if_fail (GTK_SOURCE_IS_SNIPPET (self));
+ current = self->current_chunk;
+
for (guint i = 0; i < self->chunks->len; i++)
{
GtkSourceSnippetChunk *chunk = g_ptr_array_index (self->chunks, i);
@@ -754,6 +924,11 @@ gtk_source_snippet_rewrite_updated_chunks (GtkSourceSnippet *self)
const gchar *text;
gchar *real_text;
+ /* Temporarily set current chunk to help other utilities
+ * to adjust marks appropriately.
+ */
+ self->current_chunk = chunk;
+
_gtk_source_snippet_chunk_get_bounds (chunk, &begin, &end);
text = gtk_source_snippet_chunk_get_text (chunk);
@@ -761,81 +936,21 @@ gtk_source_snippet_rewrite_updated_chunks (GtkSourceSnippet *self)
if (g_strcmp0 (text, real_text) != 0)
{
- gtk_source_snippet_replace_chunk_text (self, chunk, text);
+ gtk_text_buffer_delete (self->buffer, &begin, &end);
+ gtk_source_snippet_update_marks (self);
+
+ gtk_text_buffer_insert (self->buffer, &begin, real_text, -1);
+ gtk_source_snippet_update_marks (self);
}
g_free (real_text);
}
-}
-
-static void
-gtk_source_snippet_update_marks (GtkSourceSnippet *snippet)
-{
- GtkTextBuffer *buffer;
- GtkTextMark *last_end = NULL;
-
- g_assert (GTK_SOURCE_IS_SNIPPET (snippet));
-
- buffer = GTK_TEXT_BUFFER (snippet->buffer);
-
- for (guint i = 0; i < snippet->chunks->len; i++)
- {
- GtkSourceSnippetChunk *chunk;
- GtkTextMark *begin_mark;
- GtkTextMark *end_mark;
- GtkTextIter begin;
- GtkTextIter end;
- GtkTextIter last;
- chunk = g_ptr_array_index (snippet->chunks, i);
- begin_mark = _gtk_source_snippet_chunk_get_begin_mark (chunk);
- end_mark = _gtk_source_snippet_chunk_get_end_mark (chunk);
+ if (current)
+ g_print ("!!!! reseting current chunk to %d\n",
+ gtk_source_snippet_chunk_get_focus_position (current));
- if (last_end == NULL)
- {
- last_end = end_mark;
- continue;
- }
-
- gtk_text_buffer_get_iter_at_mark (buffer, &begin, begin_mark);
- gtk_text_buffer_get_iter_at_mark (buffer, &last, last_end);
-
- /* If the begin of this chunk has come before the end
- * of the last chunk, then that mights we are empty and
- * the right gravity of the begin mark was greedily taken
- * when inserting into a previous mark. This can happen
- * when you (often intermittently) have empty chunks.
- *
- * For example, imagine 4 empty chunks:
- *
- * [][][][]
- *
- * Except in reality to GtkTextBuffer, that's more like:
- *
- * [[[[]]]]
- *
- * When the user types 't' into the first chunk we'll end up
- * with something like this:
- *
- * [[[[t]]]]
- *
- * and we need to modify things to look like this:
- *
- * [t][[[]]]
- */
-
- if (gtk_text_iter_compare (&last, &begin) > 0)
- {
- gtk_text_buffer_move_mark (buffer, begin_mark, &last);
- }
-
- if (gtk_text_iter_compare (&begin, &end) > 0)
- {
- gtk_text_buffer_move_mark (buffer, end_mark, &begin);
- }
-
- last_end = end_mark;
- }
+ self->current_chunk = current;
}
void
@@ -850,6 +965,10 @@ _gtk_source_snippet_before_insert_text (GtkSourceSnippet *self,
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
g_return_if_fail (iter != NULL);
+ g_printerr ("Insert: %u:%u text=%s\n",
+ gtk_text_iter_get_line (iter),
+ gtk_text_iter_get_line_offset (iter),
+ text);
}
void
@@ -860,31 +979,42 @@ _gtk_source_snippet_after_insert_text (GtkSourceSnippet *self,
gint len)
{
GtkSourceSnippetChunk *chunk;
- GtkTextMark *end_mark;
- GtkTextIter end;
g_return_if_fail (GTK_SOURCE_IS_SNIPPET (self));
g_return_if_fail (self->current_chunk != NULL);
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
g_return_if_fail (iter != NULL);
- chunk = get_chunk_at_iter (self, iter);
+ /* This function is guaranteed to only be called once for the
+ * actual insert by gtksourceview-snippets.c. That allows us
+ * to update marks, update the context for shared variables, and
+ * delete/insert text in linked chunks.
+ */
- end_mark = _gtk_source_snippet_chunk_get_end_mark (chunk);
- gtk_text_buffer_get_iter_at_mark (buffer, &end, end_mark);
+ gtk_source_snippet_save_insert (self);
- if (gtk_text_iter_compare (iter, &end) > 0)
- {
- gtk_text_buffer_move_mark (buffer, end_mark, iter);
- }
+ /* First we want to update marks from the inserted text */
+ gtk_source_snippet_update_marks (self);
+ /* Now save the modified text for the iter in question */
+ chunk = get_chunk_at_iter (self, iter);
_gtk_source_snippet_chunk_save_text (chunk);
- gtk_source_snippet_update_marks (self);
+ /* Update the context (two passes to ensure that we handle chunks
+ * referencing chunks which come after themselves in the array).
+ */
gtk_source_snippet_update_context (self);
gtk_source_snippet_update_context (self);
+
+ /* Now go and rewrite each chunk that has changed. This may also
+ * update marks after each pass so that text marks don't overlap.
+ */
gtk_source_snippet_rewrite_updated_chunks (self);
+
+ /* Now we can apply tags for the given chunks */
gtk_source_snippet_update_tags (self);
+
+ gtk_source_snippet_restore_insert (self);
}
void
@@ -893,12 +1023,21 @@ _gtk_source_snippet_before_delete_range (GtkSourceSnippet *self,
GtkTextIter *begin,
GtkTextIter *end)
{
+ gchar *text;
g_return_if_fail (GTK_SOURCE_IS_SNIPPET (self));
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
g_return_if_fail (begin != NULL);
g_return_if_fail (end != NULL);
+ text = gtk_text_iter_get_slice (begin, end);
+ g_printerr ("Deleging: %u:%u to %u:%u text=%s\n",
+ gtk_text_iter_get_line (begin),
+ gtk_text_iter_get_line_offset (begin),
+ gtk_text_iter_get_line (end),
+ gtk_text_iter_get_line_offset (end),
+ text);
+ g_free (text);
}
void
@@ -907,15 +1046,36 @@ _gtk_source_snippet_after_delete_range (GtkSourceSnippet *self,
GtkTextIter *begin,
GtkTextIter *end)
{
+ GtkSourceSnippetChunk *chunk;
+
g_return_if_fail (GTK_SOURCE_IS_SNIPPET (self));
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
g_return_if_fail (begin);
g_return_if_fail (end);
+ gtk_source_snippet_save_insert (self);
+
+ /* First update mark positions based on the deletions */
+ gtk_source_snippet_update_marks (self);
+
+ /* Now save the modified text for the iter in question */
+ chunk = get_chunk_at_iter (self, begin);
+ _gtk_source_snippet_chunk_save_text (chunk);
+
+ /* Update the context (two passes to ensure that we handle chunks
+ * referencing chunks which come after themselves in the array).
+ */
gtk_source_snippet_update_context (self);
gtk_source_snippet_update_context (self);
+
+ /* Now go and rewrite each chunk that has changed. This may also
+ * update marks after each pass so that text marks don't overlap.
+ */
gtk_source_snippet_rewrite_updated_chunks (self);
+
gtk_source_snippet_update_tags (self);
+
+ gtk_source_snippet_restore_insert (self);
}
GtkTextMark *
diff --git a/gtksourceview/gtksourcesnippetchunk-private.h b/gtksourceview/gtksourcesnippetchunk-private.h
index 904302c0..071b7cbb 100644
--- a/gtksourceview/gtksourcesnippetchunk-private.h
+++ b/gtksourceview/gtksourcesnippetchunk-private.h
@@ -32,7 +32,7 @@ void _gtk_source_snippet_chunk_set_end_mark (GtkSourceSnippetChunk *ch
void _gtk_source_snippet_chunk_save_text (GtkSourceSnippetChunk *chunk);
gboolean _gtk_source_snippet_chunk_contains (GtkSourceSnippetChunk *chunk,
const GtkTextIter *iter);
-void _gtk_source_snippet_chunk_get_bounds (GtkSourceSnippetChunk *chunk,
+gboolean _gtk_source_snippet_chunk_get_bounds (GtkSourceSnippetChunk *chunk,
GtkTextIter *begin,
GtkTextIter *end);
diff --git a/gtksourceview/gtksourcesnippetchunk.c b/gtksourceview/gtksourcesnippetchunk.c
index 79f45208..9606572b 100644
--- a/gtksourceview/gtksourcesnippetchunk.c
+++ b/gtksourceview/gtksourcesnippetchunk.c
@@ -432,7 +432,7 @@ _gtk_source_snippet_chunk_get_end_mark (GtkSourceSnippetChunk *chunk)
{
g_return_val_if_fail (GTK_SOURCE_IS_SNIPPET_CHUNK (chunk), NULL);
- return chunk->begin_mark;
+ return chunk->end_mark;
}
void
@@ -455,21 +455,28 @@ _gtk_source_snippet_chunk_set_end_mark (GtkSourceSnippetChunk *chunk,
g_set_object (&chunk->end_mark, end_mark);
}
-void
+gboolean
_gtk_source_snippet_chunk_get_bounds (GtkSourceSnippetChunk *chunk,
GtkTextIter *begin,
GtkTextIter *end)
{
GtkTextBuffer *buffer;
- g_return_if_fail (GTK_SOURCE_IS_SNIPPET_CHUNK (chunk));
- g_return_if_fail (begin != NULL);
- g_return_if_fail (end != NULL);
+ g_return_val_if_fail (GTK_SOURCE_IS_SNIPPET_CHUNK (chunk), FALSE);
+ g_return_val_if_fail (begin != NULL, FALSE);
+ g_return_val_if_fail (end != NULL, FALSE);
+
+ if (chunk->begin_mark == NULL || chunk->end_mark == NULL)
+ {
+ return FALSE;
+ }
buffer = gtk_text_mark_get_buffer (chunk->begin_mark);
gtk_text_buffer_get_iter_at_mark (buffer, begin, chunk->begin_mark);
gtk_text_buffer_get_iter_at_mark (buffer, end, chunk->end_mark);
+
+ return TRUE;
}
void
@@ -509,15 +516,11 @@ _gtk_source_snippet_chunk_contains (GtkSourceSnippetChunk *chunk,
g_return_val_if_fail (GTK_SOURCE_IS_SNIPPET_CHUNK (chunk), FALSE);
g_return_val_if_fail (iter != NULL, FALSE);
- _gtk_source_snippet_chunk_get_bounds (chunk, &begin, &end);
-
-#if 0
- g_print ("Is %d between %d and %d\n",
- gtk_text_iter_get_offset (iter),
- gtk_text_iter_get_offset (&begin),
- gtk_text_iter_get_offset (&end));
-#endif
+ if (_gtk_source_snippet_chunk_get_bounds (chunk, &begin, &end))
+ {
+ return gtk_text_iter_compare (&begin, iter) <= 0 &&
+ gtk_text_iter_compare (iter, &end) <= 0;
+ }
- return gtk_text_iter_compare (iter, &begin) >= 0 &&
- gtk_text_iter_compare (iter, &end) <= 0;
+ return FALSE;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]