[gtksourceview/wip/chergert/vim] add rudimentary jumplist implementation
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtksourceview/wip/chergert/vim] add rudimentary jumplist implementation
- Date: Fri, 12 Nov 2021 00:39:06 +0000 (UTC)
commit e8ed84fa7a6772498c51d0049f8b4c7e0677e77b
Author: Christian Hergert <chergert redhat com>
Date: Thu Nov 11 16:39:00 2021 -0800
add rudimentary jumplist implementation
this does not go across files currently, and that would probably take
additional work since you copy a jump list when creating a new frame in
vim.
gtksourceview/meson.build | 1 +
gtksourceview/vim/gtk-source-vim-command.c | 31 ++++
gtksourceview/vim/gtk-source-vim-jumplist.c | 260 ++++++++++++++++++++++++++++
gtksourceview/vim/gtk-source-vim-jumplist.h | 41 +++++
gtksourceview/vim/gtk-source-vim-motion.c | 10 ++
gtksourceview/vim/gtk-source-vim-normal.c | 11 +-
gtksourceview/vim/gtk-source-vim-state.c | 64 +++++++
gtksourceview/vim/gtk-source-vim-state.h | 6 +
gtksourceview/vim/gtk-source-vim.c | 4 +
9 files changed, 427 insertions(+), 1 deletion(-)
---
diff --git a/gtksourceview/meson.build b/gtksourceview/meson.build
index c2e3e7f2..d52bf7c4 100644
--- a/gtksourceview/meson.build
+++ b/gtksourceview/meson.build
@@ -142,6 +142,7 @@ core_private_c = files([
'vim/gtk-source-vim-command.c',
'vim/gtk-source-vim-insert.c',
'vim/gtk-source-vim-insert-literal.c',
+ 'vim/gtk-source-vim-jumplist.c',
'vim/gtk-source-vim-marks.c',
'vim/gtk-source-vim-motion.c',
'vim/gtk-source-vim-normal.c',
diff --git a/gtksourceview/vim/gtk-source-vim-command.c b/gtksourceview/vim/gtk-source-vim-command.c
index 2c865bb2..2f1f3202 100644
--- a/gtksourceview/vim/gtk-source-vim-command.c
+++ b/gtksourceview/vim/gtk-source-vim-command.c
@@ -35,6 +35,7 @@
#include "gtk-source-vim.h"
#include "gtk-source-vim-char-pending.h"
#include "gtk-source-vim-command.h"
+#include "gtk-source-vim-jumplist.h"
#include "gtk-source-vim-registers.h"
typedef void (*Command) (GtkSourceVimCommand *self);
@@ -1158,6 +1159,34 @@ gtk_source_vim_command_repeat (GtkSourceVimState *state)
gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (buffer), mark);
}
+static void
+gtk_source_vim_command_jump_backward (GtkSourceVimCommand *self)
+{
+ GtkTextIter iter;
+
+ g_assert (GTK_SOURCE_IS_VIM_COMMAND (self));
+
+ if (gtk_source_vim_state_jump_backward (GTK_SOURCE_VIM_STATE (self), &iter))
+ {
+ gtk_source_vim_state_select (GTK_SOURCE_VIM_STATE (self), &iter, &iter);
+ self->ignore_mark = TRUE;
+ }
+}
+
+static void
+gtk_source_vim_command_jump_forward (GtkSourceVimCommand *self)
+{
+ GtkTextIter iter;
+
+ g_assert (GTK_SOURCE_IS_VIM_COMMAND (self));
+
+ if (gtk_source_vim_state_jump_forward (GTK_SOURCE_VIM_STATE (self), &iter))
+ {
+ gtk_source_vim_state_select (GTK_SOURCE_VIM_STATE (self), &iter, &iter);
+ self->ignore_mark = TRUE;
+ }
+}
+
static void
gtk_source_vim_command_enter (GtkSourceVimState *state)
{
@@ -1379,6 +1408,8 @@ gtk_source_vim_command_class_init (GtkSourceVimCommandClass *klass)
ADD_COMMAND ("search", gtk_source_vim_command_search);
ADD_COMMAND ("search-replace", gtk_source_vim_command_search_replace);
ADD_COMMAND ("search-reverse", gtk_source_vim_command_search_reverse);
+ ADD_COMMAND ("jump-backward", gtk_source_vim_command_jump_backward);
+ ADD_COMMAND ("jump-forward", gtk_source_vim_command_jump_forward);
#undef ADD_COMMAND
g_ptr_array_sort (commands_sorted, sort_longest_first);
diff --git a/gtksourceview/vim/gtk-source-vim-jumplist.c b/gtksourceview/vim/gtk-source-vim-jumplist.c
new file mode 100644
index 00000000..0a7bf861
--- /dev/null
+++ b/gtksourceview/vim/gtk-source-vim-jumplist.c
@@ -0,0 +1,260 @@
+/*
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2021 Christian Hergert <chergert redhat com>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include "gtk-source-vim-jumplist.h"
+
+#define MAX_JUMPS 100
+
+typedef struct
+{
+ GList link;
+ GtkTextMark *mark;
+} Jump;
+
+struct _GtkSourceVimJumplist
+{
+ GtkSourceVimState parent_instance;
+ GQueue back;
+ GQueue forward;
+};
+
+G_DEFINE_TYPE (GtkSourceVimJumplist, gtk_source_vim_jumplist, GTK_SOURCE_TYPE_VIM_STATE)
+
+GtkSourceVimState *
+gtk_source_vim_jumplist_new (void)
+{
+ return g_object_new (GTK_SOURCE_TYPE_VIM_JUMPLIST, NULL);
+}
+
+static void
+jump_free (Jump *j)
+{
+ g_assert (j->link.data == j);
+ g_assert (j->link.prev == NULL);
+ g_assert (j->link.next == NULL);
+
+ j->link.data = NULL;
+
+ if (j->mark != NULL)
+ {
+ GtkTextBuffer *buffer = gtk_text_mark_get_buffer (j->mark);
+ gtk_text_buffer_delete_mark (buffer, j->mark);
+ g_object_unref (j->mark);
+ j->mark = NULL;
+ }
+
+ g_slice_free (Jump, j);
+}
+
+static gboolean
+jump_equal (const Jump *a,
+ const Jump *b)
+{
+ GtkTextIter ai, bi;
+
+ g_assert (GTK_IS_TEXT_MARK (a->mark));
+ g_assert (GTK_IS_TEXT_MARK (b->mark));
+
+ if (a == b)
+ return TRUE;
+
+ if (a->mark == b->mark)
+ return TRUE;
+
+ gtk_text_buffer_get_iter_at_mark (gtk_text_mark_get_buffer (a->mark), &ai, a->mark);
+ gtk_text_buffer_get_iter_at_mark (gtk_text_mark_get_buffer (b->mark), &bi, b->mark);
+
+ if (gtk_text_iter_get_line (&ai) == gtk_text_iter_get_line (&bi))
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+clear_queue (GQueue *q)
+{
+ while (q->length > 0)
+ {
+ Jump *head = q->head->data;
+ g_queue_unlink (q, &head->link);
+ jump_free (head);
+ }
+}
+
+static void
+gtk_source_vim_jumplist_dispose (GObject *object)
+{
+ GtkSourceVimJumplist *self = (GtkSourceVimJumplist *)object;
+
+ clear_queue (&self->back);
+ clear_queue (&self->forward);
+
+ G_OBJECT_CLASS (gtk_source_vim_jumplist_parent_class)->dispose (object);
+}
+
+static void
+gtk_source_vim_jumplist_class_init (GtkSourceVimJumplistClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gtk_source_vim_jumplist_dispose;
+}
+
+static void
+gtk_source_vim_jumplist_init (GtkSourceVimJumplist *self)
+{
+}
+
+void
+gtk_source_vim_jumplist_push (GtkSourceVimJumplist *self,
+ const GtkTextIter *iter)
+{
+ GtkTextBuffer *buffer;
+ Jump *j;
+
+ g_return_if_fail (GTK_SOURCE_IS_VIM_JUMPLIST (self));
+ g_return_if_fail (iter != NULL);
+
+ buffer = gtk_text_iter_get_buffer (iter);
+
+ j = g_slice_new0 (Jump);
+ j->link.data = j;
+ j->mark = g_object_ref (gtk_text_buffer_create_mark (buffer, NULL, iter, TRUE));
+
+ g_assert (GTK_IS_TEXT_MARK (j->mark));
+
+ for (const GList *item = self->back.tail; item; item = item->prev)
+ {
+ Jump *j2 = item->data;
+
+ if (jump_equal (j, j2))
+ {
+ g_queue_unlink (&self->back, &j2->link);
+ jump_free (j2);
+ goto push;
+ }
+ }
+
+ for (const GList *item = self->forward.head; item; item = item->next)
+ {
+ Jump *j2 = item->data;
+
+ if (jump_equal (j, j2))
+ {
+ g_queue_unlink (&self->forward, &j2->link);
+ jump_free (j2);
+ goto push;
+ }
+ }
+
+push:
+ if (self->back.length + self->forward.length >= MAX_JUMPS)
+ {
+ if (self->back.length > 0)
+ {
+ Jump *head = self->back.head->data;
+ g_queue_unlink (&self->back, &head->link);
+ jump_free (head);
+ }
+ else
+ {
+ Jump *tail = self->forward.tail->data;
+ g_queue_unlink (&self->forward, &tail->link);
+ jump_free (tail);
+ }
+ }
+
+ g_queue_push_tail_link (&self->back, &j->link);
+}
+
+gboolean
+gtk_source_vim_jumplist_previous (GtkSourceVimJumplist *self,
+ GtkTextIter *iter)
+{
+ GtkSourceBuffer *buffer;
+ GtkTextIter before;
+ Jump current = {0};
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (GTK_SOURCE_IS_VIM_JUMPLIST (self), FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ buffer = gtk_source_vim_state_get_buffer (GTK_SOURCE_VIM_STATE (self), &before, NULL);
+
+ current.mark = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer));
+ current.link.data = ¤t;
+
+ gtk_source_vim_jumplist_push (self, &before);
+
+ while (!ret && self->back.length > 0)
+ {
+ Jump *j = g_queue_peek_tail (&self->back);
+
+ if (!jump_equal (¤t, j))
+ {
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer), iter, j->mark);
+ ret = TRUE;
+ }
+
+ g_queue_unlink (&self->back, &j->link);
+ g_queue_push_head_link (&self->forward, &j->link);
+ }
+
+ return ret;
+}
+
+gboolean
+gtk_source_vim_jumplist_next (GtkSourceVimJumplist *self,
+ GtkTextIter *iter)
+{
+ GtkSourceBuffer *buffer;
+ GtkTextIter before;
+ Jump current = {0};
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (GTK_SOURCE_IS_VIM_JUMPLIST (self), FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ buffer = gtk_source_vim_state_get_buffer (GTK_SOURCE_VIM_STATE (self), &before, NULL);
+
+ current.mark = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer));
+ current.link.data = ¤t;
+
+ gtk_source_vim_jumplist_push (self, &before);
+
+ while (!ret && self->forward.length > 0)
+ {
+ Jump *j = g_queue_peek_head (&self->forward);
+
+ if (!jump_equal (¤t, j))
+ {
+ gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer), iter, j->mark);
+ ret = TRUE;
+ }
+
+ g_queue_unlink (&self->forward, &j->link);
+ g_queue_push_tail_link (&self->back, &j->link);
+ }
+
+ return ret;
+}
diff --git a/gtksourceview/vim/gtk-source-vim-jumplist.h b/gtksourceview/vim/gtk-source-vim-jumplist.h
new file mode 100644
index 00000000..f35b892a
--- /dev/null
+++ b/gtksourceview/vim/gtk-source-vim-jumplist.h
@@ -0,0 +1,41 @@
+/*
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2021 Christian Hergert <chergert redhat com>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#pragma once
+
+#include "gtk-source-vim-state.h"
+
+G_BEGIN_DECLS
+
+#define GTK_SOURCE_TYPE_VIM_JUMPLIST (gtk_source_vim_jumplist_get_type())
+
+G_DECLARE_FINAL_TYPE (GtkSourceVimJumplist, gtk_source_vim_jumplist, GTK_SOURCE, VIM_JUMPLIST,
GtkSourceVimState)
+
+GtkSourceVimState *gtk_source_vim_jumplist_new (void);
+void gtk_source_vim_jumplist_push (GtkSourceVimJumplist *self,
+ const GtkTextIter *iter);
+gboolean gtk_source_vim_jumplist_previous (GtkSourceVimJumplist *self,
+ GtkTextIter *iter);
+gboolean gtk_source_vim_jumplist_next (GtkSourceVimJumplist *self,
+ GtkTextIter *iter);
+
+
+G_END_DECLS
diff --git a/gtksourceview/vim/gtk-source-vim-motion.c b/gtksourceview/vim/gtk-source-vim-motion.c
index 10993433..a58eb3ee 100644
--- a/gtksourceview/vim/gtk-source-vim-motion.c
+++ b/gtksourceview/vim/gtk-source-vim-motion.c
@@ -1622,6 +1622,7 @@ gtk_source_vim_motion_handle_keypress (GtkSourceVimState *state,
switch (keyval)
{
case GDK_KEY_g:
+ self->is_jump = TRUE;
return gtk_source_vim_motion_complete (self, motion_buffer_start_first_char,
INCLUSIVE, LINEWISE);
case GDK_KEY_e:
@@ -1955,6 +1956,15 @@ gtk_source_vim_motion_leave (GtkSourceVimState *state)
if (self->apply_on_leave)
{
+ /* If this motion is a jump, then add it to the jumplist */
+ if (self->is_jump)
+ {
+ GtkTextIter origin;
+
+ gtk_source_vim_state_get_buffer (state, &origin, NULL);
+ gtk_source_vim_state_push_jump (state, &origin);
+ }
+
gtk_source_vim_motion_repeat (state);
}
}
diff --git a/gtksourceview/vim/gtk-source-vim-normal.c b/gtksourceview/vim/gtk-source-vim-normal.c
index bc02fcfe..c51a2328 100644
--- a/gtksourceview/vim/gtk-source-vim-normal.c
+++ b/gtksourceview/vim/gtk-source-vim-normal.c
@@ -1126,6 +1126,14 @@ key_handler_initial (GtkSourceVimNormal *self,
self->handler = key_handler_command;
break;
+ case GDK_KEY_o:
+ gtk_source_vim_normal_begin_command (self, NULL, NULL, "jump-backward", 0);
+ return TRUE;
+
+ case GDK_KEY_i:
+ gtk_source_vim_normal_begin_command (self, NULL, NULL, "jump-forward", 0);
+ return TRUE;
+
default:
break;
}
@@ -1326,12 +1334,13 @@ gtk_source_vim_normal_resume (GtkSourceVimState *state,
GtkSourceBuffer *buffer;
GtkSourceView *view;
GtkTextMark *insert;
+ GtkTextIter origin;
gboolean unparent = TRUE;
g_assert (GTK_SOURCE_IS_VIM_NORMAL (self));
g_assert (GTK_SOURCE_IS_VIM_STATE (from));
- buffer = gtk_source_vim_state_get_buffer (state, NULL, NULL);
+ buffer = gtk_source_vim_state_get_buffer (state, &origin, NULL);
insert = gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer));
view = gtk_source_vim_state_get_view (state);
diff --git a/gtksourceview/vim/gtk-source-vim-state.c b/gtksourceview/vim/gtk-source-vim-state.c
index d73fcfa4..0063d737 100644
--- a/gtksourceview/vim/gtk-source-vim-state.c
+++ b/gtksourceview/vim/gtk-source-vim-state.c
@@ -29,6 +29,7 @@
#include "gtksourceutils-private.h"
#include "gtksourceview.h"
+#include "gtk-source-vim-jumplist.h"
#include "gtk-source-vim-registers.h"
#include "gtk-source-vim-marks.h"
#include "gtk-source-vim-state.h"
@@ -38,6 +39,7 @@ typedef struct
/* Owned reference to marks/registers (usually set low in the stack) */
GtkSourceVimState *registers;
GtkSourceVimState *marks;
+ GtkSourceVimState *jumplist;
/* Owned reference to the view (usually set low in the stack) */
GtkSourceView *view;
@@ -289,6 +291,7 @@ gtk_source_vim_state_dispose (GObject *object)
g_clear_weak_pointer (&priv->view);
gtk_source_vim_state_release (&priv->registers);
gtk_source_vim_state_release (&priv->marks);
+ gtk_source_vim_state_release (&priv->jumplist);
/* First remove the children from our list */
while (priv->children.length > 0)
@@ -1386,3 +1389,64 @@ gtk_source_vim_state_get_iter_at_mark (GtkSourceVimState *self,
return gtk_source_vim_marks_get_iter (marks, name, iter);
}
+
+static GtkSourceVimJumplist *
+gtk_source_vim_state_get_jumplist (GtkSourceVimState *self)
+{
+ GtkSourceVimState *root;
+ GtkSourceVimStatePrivate *priv;
+
+ g_assert (GTK_SOURCE_IS_VIM_STATE (self));
+
+ root = gtk_source_vim_state_get_root (self);
+ priv = gtk_source_vim_state_get_instance_private (root);
+
+ if (priv->jumplist == NULL)
+ {
+ priv->jumplist = gtk_source_vim_jumplist_new ();
+ gtk_source_vim_state_set_parent (priv->jumplist, root);
+ }
+
+ return GTK_SOURCE_VIM_JUMPLIST (priv->jumplist);
+}
+
+void
+gtk_source_vim_state_push_jump (GtkSourceVimState *self,
+ const GtkTextIter *iter)
+{
+ GtkSourceVimJumplist *jumplist;
+
+ g_return_if_fail (GTK_SOURCE_IS_VIM_STATE (self));
+ g_return_if_fail (iter != NULL);
+
+ jumplist = gtk_source_vim_state_get_jumplist (self);
+ gtk_source_vim_jumplist_push (jumplist, iter);
+}
+
+gboolean
+gtk_source_vim_state_jump_backward (GtkSourceVimState *self,
+ GtkTextIter *iter)
+{
+ GtkSourceVimJumplist *jumplist;
+
+ g_return_val_if_fail (GTK_SOURCE_IS_VIM_STATE (self), FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ jumplist = gtk_source_vim_state_get_jumplist (self);
+
+ return gtk_source_vim_jumplist_previous (jumplist, iter);
+}
+
+gboolean
+gtk_source_vim_state_jump_forward (GtkSourceVimState *self,
+ GtkTextIter *iter)
+{
+ GtkSourceVimJumplist *jumplist;
+
+ g_return_val_if_fail (GTK_SOURCE_IS_VIM_STATE (self), FALSE);
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ jumplist = gtk_source_vim_state_get_jumplist (self);
+
+ return gtk_source_vim_jumplist_next (jumplist, iter);
+}
diff --git a/gtksourceview/vim/gtk-source-vim-state.h b/gtksourceview/vim/gtk-source-vim-state.h
index 3ecce7d9..d8bf8405 100644
--- a/gtksourceview/vim/gtk-source-vim-state.h
+++ b/gtksourceview/vim/gtk-source-vim-state.h
@@ -138,6 +138,12 @@ void gtk_source_vim_state_keyval_to_string (guint
void gtk_source_vim_state_keyval_unescaped (guint keyval,
GdkModifierType mods,
char string[16]);
+void gtk_source_vim_state_push_jump (GtkSourceVimState *self,
+ const GtkTextIter *iter);
+gboolean gtk_source_vim_state_jump_backward (GtkSourceVimState *self,
+ GtkTextIter *iter);
+gboolean gtk_source_vim_state_jump_forward (GtkSourceVimState *self,
+ GtkTextIter *iter);
static inline void
gtk_source_vim_state_release (GtkSourceVimState **dest)
diff --git a/gtksourceview/vim/gtk-source-vim.c b/gtksourceview/vim/gtk-source-vim.c
index ad1cdc0c..0ea22bf6 100644
--- a/gtksourceview/vim/gtk-source-vim.c
+++ b/gtksourceview/vim/gtk-source-vim.c
@@ -204,11 +204,13 @@ gtk_source_vim_view_set (GtkSourceVimState *state)
{
GtkSourceVim *self = (GtkSourceVim *)state;
GtkSourceView *view;
+ GtkTextIter iter;
g_assert (GTK_SOURCE_IS_VIM (self));
g_assert (gtk_source_vim_state_get_child (state) == NULL);
view = gtk_source_vim_state_get_view (state);
+ gtk_source_vim_state_get_buffer (state, &iter, NULL);
g_signal_connect_object (view,
"notify::buffer",
@@ -217,6 +219,8 @@ gtk_source_vim_view_set (GtkSourceVimState *state)
G_CONNECT_SWAPPED);
on_notify_buffer_cb (self, NULL, view);
+ gtk_source_vim_state_push_jump (state, &iter);
+
gtk_source_vim_state_push (state, gtk_source_vim_normal_new ());
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]