[gnome-builder] libide/sourceview: implement insert matching brace
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] libide/sourceview: implement insert matching brace
- Date: Tue, 6 Sep 2022 05:47:08 +0000 (UTC)
commit ed03737f14f877b5bb223f6750a765bc017b04b5
Author: Christian Hergert <chergert redhat com>
Date: Mon Sep 5 22:46:44 2022 -0700
libide/sourceview: implement insert matching brace
Sadly, this doesn't work with vim mode though because it passes events to
the GtkTextView IM method which seems to always filter regular key
presses like ' or ".
src/libide/sourceview/ide-source-view-private.h | 1 +
src/libide/sourceview/ide-source-view.c | 169 ++++++++++++++++++++++++
src/libide/sourceview/ide-source-view.h | 63 +++++----
3 files changed, 204 insertions(+), 29 deletions(-)
---
diff --git a/src/libide/sourceview/ide-source-view-private.h b/src/libide/sourceview/ide-source-view-private.h
index 92eff4d1a..8f9b8722f 100644
--- a/src/libide/sourceview/ide-source-view-private.h
+++ b/src/libide/sourceview/ide-source-view-private.h
@@ -72,6 +72,7 @@ struct _IdeSourceView
/* Bitfield values go here */
guint highlight_current_line : 1;
+ guint insert_matching_brace : 1;
};
diff --git a/src/libide/sourceview/ide-source-view.c b/src/libide/sourceview/ide-source-view.c
index c1948e820..d51be41fc 100644
--- a/src/libide/sourceview/ide-source-view.c
+++ b/src/libide/sourceview/ide-source-view.c
@@ -33,6 +33,7 @@ enum {
PROP_0,
PROP_FONT_DESC,
PROP_FONT_SCALE,
+ PROP_INSERT_MATCHING_BRACE,
PROP_LINE_HEIGHT,
PROP_ZOOM_LEVEL,
N_PROPS,
@@ -260,6 +261,122 @@ ide_source_view_click_pressed_cb (IdeSourceView *self,
IDE_EXIT;
}
+static gboolean
+ide_source_view_maybe_delete_match (IdeSourceView *self)
+{
+ GtkTextBuffer *buffer;
+ GtkTextMark *insert;
+ GtkTextIter iter;
+ GtkTextIter prev;
+ gunichar ch;
+ gunichar match;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
+ insert = gtk_text_buffer_get_insert (buffer);
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
+ prev = iter;
+ if (!gtk_text_iter_backward_char (&prev))
+ return FALSE;
+
+ ch = gtk_text_iter_get_char (&prev);
+
+ switch (ch)
+ {
+ case '[': match = ']'; break;
+ case '{': match = '}'; break;
+ case '(': match = ')'; break;
+ case '"': match = '"'; break;
+ case '\'': match = '\''; break;
+ case '<': match = '>'; break;
+ default: match = 0; break;
+ }
+
+ if (match && (gtk_text_iter_get_char (&iter) == match))
+ {
+ gtk_text_iter_forward_char (&iter);
+ gtk_text_buffer_delete (buffer, &prev, &iter);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+ide_source_view_key_pressed_cb (IdeSourceView *self,
+ guint keyval,
+ guint keycode,
+ GdkModifierType state,
+ GtkEventControllerKey *key)
+{
+ GtkTextBuffer *buffer;
+
+ g_assert (IDE_IS_SOURCE_VIEW (self));
+ g_assert (GTK_IS_EVENT_CONTROLLER_KEY (key));
+
+ buffer = GTK_TEXT_BUFFER (self->buffer);
+
+ if (self->insert_matching_brace &&
+ !gtk_text_buffer_get_has_selection (buffer))
+ {
+ const char *insert = NULL;
+ GtkTextIter iter;
+
+ if (keyval == GDK_KEY_BackSpace)
+ {
+ if (ide_source_view_maybe_delete_match (self))
+ return TRUE;
+ }
+
+ gtk_text_buffer_get_selection_bounds (buffer, &iter, NULL);
+
+ switch (keyval)
+ {
+ case GDK_KEY_bracketleft:
+ insert = "[]";
+ break;
+
+ case GDK_KEY_braceleft:
+ insert = "{}";
+ break;
+
+ case GDK_KEY_apostrophe:
+ insert = "''";
+ break;
+
+ case GDK_KEY_parenleft:
+ insert = "()";
+ break;
+
+ case GDK_KEY_quotedbl:
+ insert = "\"\"";
+ break;
+
+ case GDK_KEY_less:
+ insert = "<>";
+ break;
+
+ default:
+ break;
+ }
+
+ if (insert)
+ {
+ gtk_text_buffer_begin_user_action (buffer);
+ gtk_text_buffer_insert (buffer, &iter, insert, -1);
+ gtk_text_iter_backward_char (&iter);
+ gtk_text_buffer_select_range (buffer, &iter, &iter);
+ gtk_text_buffer_end_user_action (buffer);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
static void
ide_source_view_buffer_request_scroll_to_insert_cb (IdeSourceView *self,
IdeBuffer *buffer)
@@ -881,6 +998,10 @@ ide_source_view_get_property (GObject *object,
g_value_set_boolean (value, ide_source_view_get_highlight_current_line (self));
break;
+ case PROP_INSERT_MATCHING_BRACE:
+ g_value_set_boolean (value, ide_source_view_get_insert_matching_brace (self));
+ break;
+
case PROP_LINE_HEIGHT:
g_value_set_double (value, self->line_height);
break;
@@ -918,6 +1039,10 @@ ide_source_view_set_property (GObject *object,
ide_source_view_set_highlight_current_line (self, g_value_get_boolean (value));
break;
+ case PROP_INSERT_MATCHING_BRACE:
+ ide_source_view_set_insert_matching_brace (self, g_value_get_boolean (value));
+ break;
+
case PROP_LINE_HEIGHT:
ide_source_view_set_line_height (self, g_value_get_double (value));
break;
@@ -968,6 +1093,13 @@ ide_source_view_class_init (IdeSourceViewClass *klass)
G_MININT, G_MAXINT, 0,
(G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+ properties [PROP_INSERT_MATCHING_BRACE] =
+ g_param_spec_boolean ("insert-matching-brace",
+ "Insert Matching Brace",
+ "Insert a matching brace/bracket/quotation/parenthesis",
+ FALSE,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
properties [PROP_ZOOM_LEVEL] =
g_param_spec_double ("zoom-level",
"Zoom Level",
@@ -1020,6 +1152,7 @@ ide_source_view_init (IdeSourceView *self)
GtkEventController *click;
GtkEventController *focus;
GtkEventController *scroll;
+ GtkEventController *key;
g_signal_connect (self,
"notify::buffer",
@@ -1035,6 +1168,7 @@ ide_source_view_init (IdeSourceView *self)
/* Setup a handler to emit ::populate-menu */
click = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ());
+ gtk_event_controller_set_name (click, "ide-source-view-click");
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (click), 0);
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (click),
GTK_PHASE_CAPTURE);
@@ -1044,8 +1178,19 @@ ide_source_view_init (IdeSourceView *self)
self);
gtk_widget_add_controller (GTK_WIDGET (self), click);
+ /* Key handler for internal features like insert-matching-braces */
+ key = gtk_event_controller_key_new ();
+ gtk_event_controller_set_name (key, "ide-source-view-key");
+ gtk_event_controller_set_propagation_phase (key, GTK_PHASE_CAPTURE);
+ g_signal_connect_swapped (key,
+ "key-pressed",
+ G_CALLBACK (ide_source_view_key_pressed_cb),
+ self);
+ gtk_widget_add_controller (GTK_WIDGET (self), key);
+
/* Setup focus tracking */
focus = gtk_event_controller_focus_new ();
+ gtk_event_controller_set_name (focus, "ide-source-view-focus");
g_signal_connect_swapped (focus,
"enter",
G_CALLBACK (ide_source_view_focus_enter_cb),
@@ -1058,6 +1203,7 @@ ide_source_view_init (IdeSourceView *self)
/* Setup ctrl+scroll zoom */
scroll = gtk_event_controller_scroll_new (GTK_EVENT_CONTROLLER_SCROLL_VERTICAL);
+ gtk_event_controller_set_name (scroll, "ide-source-view-zoom");
gtk_event_controller_set_propagation_phase (scroll, GTK_PHASE_CAPTURE);
g_signal_connect (scroll,
"scroll",
@@ -1421,3 +1567,26 @@ ide_source_view_get_iter_at_visual_position (IdeSourceView *self,
gtk_text_iter_forward_char (iter);
}
}
+
+gboolean
+ide_source_view_get_insert_matching_brace (IdeSourceView *self)
+{
+ g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), FALSE);
+
+ return self->insert_matching_brace;
+}
+
+void
+ide_source_view_set_insert_matching_brace (IdeSourceView *self,
+ gboolean insert_matching_brace)
+{
+ g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
+
+ insert_matching_brace = !!insert_matching_brace;
+
+ if (insert_matching_brace != self->insert_matching_brace)
+ {
+ self->insert_matching_brace = insert_matching_brace;
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_INSERT_MATCHING_BRACE]);
+ }
+}
diff --git a/src/libide/sourceview/ide-source-view.h b/src/libide/sourceview/ide-source-view.h
index b36bff543..85f3a7417 100644
--- a/src/libide/sourceview/ide-source-view.h
+++ b/src/libide/sourceview/ide-source-view.h
@@ -36,47 +36,52 @@ IDE_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (IdeSourceView, ide_source_view, IDE, SOURCE_VIEW, GtkSourceView)
IDE_AVAILABLE_IN_ALL
-GtkWidget *ide_source_view_new (void);
+GtkWidget *ide_source_view_new (void);
IDE_AVAILABLE_IN_ALL
-void ide_source_view_scroll_to_insert (IdeSourceView *self);
+void ide_source_view_scroll_to_insert (IdeSourceView *self);
IDE_AVAILABLE_IN_ALL
-char *ide_source_view_dup_position_label (IdeSourceView *self);
+char *ide_source_view_dup_position_label (IdeSourceView *self);
IDE_AVAILABLE_IN_ALL
-void ide_source_view_get_visual_position (IdeSourceView *self,
- guint *line,
- guint *line_column);
+void ide_source_view_get_visual_position (IdeSourceView *self,
+ guint *line,
+ guint
*line_column);
IDE_AVAILABLE_IN_ALL
-gboolean ide_source_view_get_highlight_current_line (IdeSourceView *self);
+gboolean ide_source_view_get_highlight_current_line (IdeSourceView *self);
IDE_AVAILABLE_IN_ALL
-void ide_source_view_set_highlight_current_line (IdeSourceView *self,
- gboolean highlight_current_line);
+void ide_source_view_set_highlight_current_line (IdeSourceView *self,
+ gboolean
highlight_current_line);
IDE_AVAILABLE_IN_ALL
-double ide_source_view_get_zoom_level (IdeSourceView *self);
+double ide_source_view_get_zoom_level (IdeSourceView *self);
IDE_AVAILABLE_IN_ALL
-void ide_source_view_set_font_desc (IdeSourceView *self,
- const PangoFontDescription *font_desc);
+void ide_source_view_set_font_desc (IdeSourceView *self,
+ const PangoFontDescription
*font_desc);
IDE_AVAILABLE_IN_ALL
-const PangoFontDescription *ide_source_view_get_font_desc (IdeSourceView *self);
+const PangoFontDescription *ide_source_view_get_font_desc (IdeSourceView *self);
IDE_AVAILABLE_IN_ALL
-void ide_source_view_prepend_menu (IdeSourceView *self,
- GMenuModel *menu_model);
+void ide_source_view_prepend_menu (IdeSourceView *self,
+ GMenuModel
*menu_model);
IDE_AVAILABLE_IN_ALL
-void ide_source_view_append_menu (IdeSourceView *self,
- GMenuModel *menu_model);
+void ide_source_view_append_menu (IdeSourceView *self,
+ GMenuModel
*menu_model);
IDE_AVAILABLE_IN_ALL
-void ide_source_view_remove_menu (IdeSourceView *self,
- GMenuModel *menu_model);
+void ide_source_view_remove_menu (IdeSourceView *self,
+ GMenuModel
*menu_model);
IDE_AVAILABLE_IN_ALL
-void ide_source_view_jump_to_iter (GtkTextView *text_view,
- const GtkTextIter *iter,
- double within_margin,
- gboolean use_align,
- double xalign,
- double yalign);
+void ide_source_view_jump_to_iter (GtkTextView
*text_view,
+ const GtkTextIter *iter,
+ double
within_margin,
+ gboolean
use_align,
+ double xalign,
+ double yalign);
IDE_AVAILABLE_IN_ALL
-void ide_source_view_get_iter_at_visual_position (IdeSourceView *self,
- GtkTextIter *iter,
- guint line,
- guint line_offset);
+void ide_source_view_get_iter_at_visual_position (IdeSourceView *self,
+ GtkTextIter *iter,
+ guint line,
+ guint
line_offset);
+IDE_AVAILABLE_IN_ALL
+gboolean ide_source_view_get_insert_matching_brace (IdeSourceView *self);
+IDE_AVAILABLE_IN_ALL
+void ide_source_view_set_insert_matching_brace (IdeSourceView *self,
+ gboolean
insert_matching_brace);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]