[gtksourceview] vim: add filter (=) when in visual mode
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtksourceview] vim: add filter (=) when in visual mode
- Date: Fri, 26 Nov 2021 16:07:53 +0000 (UTC)
commit 04642e59ee8da09041eb249040f5248e2cff3b5e
Author: Christian Hergert <chergert redhat com>
Date: Fri Nov 26 10:06:19 2021 -0600
vim: add filter (=) when in visual mode
This has a fallback implementation which just re-indents the contents using
the current indenter.
gtksourceview/vim/gtksourcevim.c | 181 +++++++++++++++++++++++++++++---
gtksourceview/vim/gtksourcevim.h | 5 +-
gtksourceview/vim/gtksourcevimcommand.c | 29 +++++
gtksourceview/vim/gtksourcevimvisual.c | 3 +
4 files changed, 205 insertions(+), 13 deletions(-)
---
diff --git a/gtksourceview/vim/gtksourcevim.c b/gtksourceview/vim/gtksourcevim.c
index 4bfd78fe..cf455135 100644
--- a/gtksourceview/vim/gtksourcevim.c
+++ b/gtksourceview/vim/gtksourcevim.c
@@ -21,6 +21,7 @@
#include "config.h"
+#include "gtksourceindenter.h"
#include "gtksourceview.h"
#include "gtksourcevim.h"
@@ -51,6 +52,7 @@ enum {
enum {
EXECUTE_COMMAND,
+ FILTER,
FORMAT,
READY,
SPLIT,
@@ -70,6 +72,95 @@ gtk_source_vim_new (GtkSourceView *view)
NULL);
}
+static inline gboolean
+compare_position (const GtkTextIter *iter,
+ GtkTextMark *mark)
+{
+ GtkTextIter mark_iter;
+ gtk_text_buffer_get_iter_at_mark (gtk_text_mark_get_buffer (mark),
+ &mark_iter, mark);
+ return gtk_text_iter_compare (iter, &mark_iter);
+}
+
+static gboolean
+gtk_source_vim_real_format (GtkSourceVim *self,
+ GtkTextIter *begin,
+ GtkTextIter *end)
+{
+ return FALSE;
+}
+
+static gboolean
+gtk_source_vim_real_filter (GtkSourceVim *self,
+ GtkTextIter *begin,
+ GtkTextIter *end)
+{
+ GtkSourceIndenter *indenter;
+ GtkSourceView *view;
+ GtkTextBuffer *buffer;
+ GtkTextMark *begin_mark;
+ GtkTextMark *end_mark;
+ GtkTextIter iter;
+
+ g_assert (GTK_SOURCE_IS_VIM (self));
+ g_assert (begin != NULL);
+ g_assert (end != NULL);
+
+ buffer = gtk_text_iter_get_buffer (begin);
+ view = gtk_source_vim_state_get_view (GTK_SOURCE_VIM_STATE (self));
+
+ /* If there is no indenter, we can't do anything here */
+ if (!(indenter = gtk_source_view_get_indenter (view)))
+ {
+ return FALSE;
+ }
+
+ /* Create temporary marks for bounds checking */
+ begin_mark = gtk_text_buffer_create_mark (buffer, NULL, begin, TRUE);
+ end_mark = gtk_text_buffer_create_mark (buffer, NULL, end, FALSE);
+
+ /* Start at beginning of first line */
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, begin_mark);
+ gtk_text_iter_set_line_offset (&iter, 0);
+
+ /* Remove prefix space from each line */
+ while (compare_position (&iter, end_mark) < 0)
+ {
+ GtkTextIter end_of_space = iter;
+ guint line;
+
+ while (!gtk_text_iter_ends_line (&end_of_space) &&
+ g_unichar_isspace (gtk_text_iter_get_char (&end_of_space)))
+ {
+ gtk_text_iter_forward_char (&end_of_space);
+ }
+
+ if (!gtk_text_iter_equal (&iter, &end_of_space))
+ {
+ gtk_text_buffer_delete (buffer, &iter, &end_of_space);
+ }
+
+ /* The thought here to get_iter_at_line() instead of forward_line()
+ * is that it is a bit more resilient against how much the indenter
+ * changes outside of the direct indentation point. It ensures that
+ * we take the next line after we were on and then processes it too.
+ */
+ line = gtk_text_iter_get_line (&iter);
+ gtk_source_indenter_indent (indenter, view, &iter);
+ gtk_text_buffer_get_iter_at_line (buffer, &iter, line + 1);
+ }
+
+ /* Revalidate iter positions */
+ gtk_text_buffer_get_iter_at_mark (buffer, begin, begin_mark);
+ gtk_text_buffer_get_iter_at_mark (buffer, end, end_mark);
+
+ /* Remove our temporary marks */
+ gtk_text_buffer_delete_mark (buffer, begin_mark);
+ gtk_text_buffer_delete_mark (buffer, end_mark);
+
+ return TRUE;
+}
+
static gboolean
gtk_source_vim_handle_event (GtkSourceVimState *state,
GdkEvent *event)
@@ -341,16 +432,58 @@ gtk_source_vim_class_init (GtkSourceVimClass *klass)
* @begin: the beginning of the text range
* @end: the end of the text range
*
- * Requests that the text range @begin to @end be reformatted.
+ * Requests that the text range @begin to @end be formatted.
+ *
+ * This is equivalent to the `gq` command in Vim.
+ *
* Applications should conntect to this signal to implement
- * reformatting as they would like.
+ * formatting as they would like.
+ *
+ * Returns: %TRUE if the format request was handled; otherwise %FALSE
*/
signals[FORMAT] =
- g_signal_new ("format",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- 0, NULL, NULL, NULL,
- G_TYPE_NONE, 2, GTK_TYPE_TEXT_ITER, GTK_TYPE_TEXT_ITER);
+ g_signal_new_class_handler ("format",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_CALLBACK (gtk_source_vim_real_format),
+ g_signal_accumulator_true_handled, NULL,
+ NULL,
+ G_TYPE_BOOLEAN,
+ 2,
+ GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
+ GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+ /**
+ * GtkSourceVim::filter:
+ * @self: a #GtkSourceVim
+ * @begin: the beginning of the text range
+ * @end: the end of the text range
+ *
+ * Requests that the text range @begin to @end be filtered (transformed
+ * in some way and replaced).
+ *
+ * Applications should conntect to this signal to implement
+ * filtering as they would like.
+ *
+ * The default handler will attempt to filter by using the
+ * #GtkSourceView's indenter to reindent each line.
+ *
+ * In the future, some effort may be made to restrict line width for
+ * languages and contexts which are known to be safe.
+ *
+ * Returns: %TRUE if the filter request was handled; otherwise %FALSE
+ */
+ signals[FILTER] =
+ g_signal_new_class_handler ("filter",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_CALLBACK (gtk_source_vim_real_filter),
+ g_signal_accumulator_true_handled, NULL,
+ NULL,
+ G_TYPE_BOOLEAN,
+ 2,
+ GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
+ GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE);
signals[READY] =
g_signal_new ("ready",
@@ -494,14 +627,38 @@ gtk_source_vim_emit_ready (GtkSourceVim *self)
g_signal_emit (self, signals[READY], 0);
}
-void
+gboolean
+gtk_source_vim_emit_filter (GtkSourceVim *self,
+ GtkTextIter *begin,
+ GtkTextIter *end)
+{
+ gboolean ret;
+
+ g_return_val_if_fail (GTK_SOURCE_IS_VIM (self), FALSE);
+ g_return_val_if_fail (begin != NULL, FALSE);
+ g_return_val_if_fail (end != NULL, FALSE);
+
+ gtk_text_iter_order (begin, end);
+
+ g_signal_emit (self, signals[FILTER], 0, begin, end, &ret);
+
+ return ret;
+}
+
+gboolean
gtk_source_vim_emit_format (GtkSourceVim *self,
GtkTextIter *begin,
GtkTextIter *end)
{
- g_return_if_fail (GTK_SOURCE_IS_VIM (self));
- g_return_if_fail (begin != NULL);
- g_return_if_fail (end != NULL);
+ gboolean ret;
- g_signal_emit (self, signals[FORMAT], 0, begin, end);
+ g_return_val_if_fail (GTK_SOURCE_IS_VIM (self), FALSE);
+ g_return_val_if_fail (begin != NULL, FALSE);
+ g_return_val_if_fail (end != NULL, FALSE);
+
+ gtk_text_iter_order (begin, end);
+
+ g_signal_emit (self, signals[FORMAT], 0, begin, end, &ret);
+
+ return ret;
}
diff --git a/gtksourceview/vim/gtksourcevim.h b/gtksourceview/vim/gtksourcevim.h
index 2f48c1fa..295b757c 100644
--- a/gtksourceview/vim/gtksourcevim.h
+++ b/gtksourceview/vim/gtksourcevim.h
@@ -35,7 +35,10 @@ const char *gtk_source_vim_get_command_text (GtkSourceVim *self);
const char *gtk_source_vim_get_command_bar_text (GtkSourceVim *self);
gboolean gtk_source_vim_emit_execute_command (GtkSourceVim *self,
const char *command);
-void gtk_source_vim_emit_format (GtkSourceVim *self,
+gboolean gtk_source_vim_emit_filter (GtkSourceVim *self,
+ GtkTextIter *begin,
+ GtkTextIter *end);
+gboolean gtk_source_vim_emit_format (GtkSourceVim *self,
GtkTextIter *begin,
GtkTextIter *end);
void gtk_source_vim_emit_ready (GtkSourceVim *self);
diff --git a/gtksourceview/vim/gtksourcevimcommand.c b/gtksourceview/vim/gtksourcevimcommand.c
index d4c6aa32..b923555d 100644
--- a/gtksourceview/vim/gtksourcevimcommand.c
+++ b/gtksourceview/vim/gtksourcevimcommand.c
@@ -98,6 +98,34 @@ parse_number (const char *str,
return TRUE;
}
+static void
+gtk_source_vim_command_filter (GtkSourceVimCommand *self)
+{
+ GtkSourceVimState *root;
+ GtkSourceBuffer *buffer;
+ GtkTextIter iter;
+ GtkTextIter selection;
+
+ if (!gtk_source_vim_state_get_editable (GTK_SOURCE_VIM_STATE (self)))
+ return;
+
+ buffer = gtk_source_vim_state_get_buffer (GTK_SOURCE_VIM_STATE (self), &iter, &selection);
+ root = gtk_source_vim_state_get_root (GTK_SOURCE_VIM_STATE (self));
+
+ if (GTK_SOURCE_IS_VIM (root))
+ {
+ gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
+ gtk_source_vim_emit_filter (GTK_SOURCE_VIM (root), &iter, &selection);
+ gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
+
+ gtk_text_iter_order (&iter, &selection);
+
+ gtk_text_buffer_select_range (GTK_TEXT_BUFFER (buffer), &iter, &iter);
+ }
+
+ self->ignore_mark = TRUE;
+}
+
static void
gtk_source_vim_command_format (GtkSourceVimCommand *self)
{
@@ -1515,6 +1543,7 @@ gtk_source_vim_command_class_init (GtkSourceVimCommandClass *klass)
ADD_COMMAND ("indent", gtk_source_vim_command_indent);
ADD_COMMAND ("unindent", gtk_source_vim_command_unindent);
ADD_COMMAND ("line-number", gtk_source_vim_command_line_number);
+ ADD_COMMAND ("filter", gtk_source_vim_command_filter);
ADD_COMMAND ("format", gtk_source_vim_command_format);
ADD_COMMAND ("search", gtk_source_vim_command_search);
ADD_COMMAND ("search-replace", gtk_source_vim_command_search_replace);
diff --git a/gtksourceview/vim/gtksourcevimvisual.c b/gtksourceview/vim/gtksourcevimvisual.c
index c969b036..f0595562 100644
--- a/gtksourceview/vim/gtksourcevimvisual.c
+++ b/gtksourceview/vim/gtksourcevimvisual.c
@@ -597,6 +597,9 @@ key_handler_initial (GtkSourceVimVisual *self,
case GDK_KEY_less:
return gtk_source_vim_visual_begin_command (self, "unindent", FALSE);
+ case GDK_KEY_equal:
+ return gtk_source_vim_visual_begin_command (self, "filter", FALSE);
+
case GDK_KEY_slash:
case GDK_KEY_question:
{
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]