diff --git a/src/libide/sourceview/ide-source-view-addins.c b/src/libide/sourceview/ide-source-view-addins.c index e5124cdc2782babc46745feec0fbea4d80f3264b..af408bd7dfbeb90e51d9444d34b97d206bd446ce 100644 --- a/src/libide/sourceview/ide-source-view-addins.c +++ b/src/libide/sourceview/ide-source-view-addins.c @@ -134,6 +134,59 @@ ide_source_view_hover_provider_removed_cb (IdeExtensionSetAdapter *adapter, IDE_EXIT; } +static void +ide_source_view_indenter_added_cb (IdeExtensionSetAdapter *adapter, + PeasPluginInfo *plugin_info, + PeasExtension *exten, + gpointer user_data) +{ + GtkSourceIndenter *indenter = (GtkSourceIndenter *)exten; + IdeSourceView *self = user_data; + + IDE_ENTRY; + + g_assert (IDE_IS_EXTENSION_SET_ADAPTER (adapter)); + g_assert (plugin_info != NULL); + g_assert (GTK_SOURCE_IS_INDENTER (indenter)); + g_assert (IDE_IS_SOURCE_VIEW (self)); + + g_debug ("Adding indenter %s from module %s", + G_OBJECT_TYPE_NAME (indenter), + peas_plugin_info_get_module_name (plugin_info)); + + // TODO: how to handle multiple indenters? + gtk_source_view_set_indenter (GTK_SOURCE_VIEW (self), indenter); + + IDE_EXIT; +} + +static void +ide_source_view_indenter_removed_cb (IdeExtensionSetAdapter *adapter, + PeasPluginInfo *plugin_info, + PeasExtension *exten, + gpointer user_data) +{ + GtkSourceIndenter *indenter = (GtkSourceIndenter *)exten; + IdeSourceView *self = user_data; + + IDE_ENTRY; + + g_assert (IDE_IS_EXTENSION_SET_ADAPTER (adapter)); + g_assert (plugin_info != NULL); + g_assert (GTK_SOURCE_IS_INDENTER (indenter)); + g_assert (IDE_IS_SOURCE_VIEW (self)); + + g_debug ("Removing indenter %s from module %s", + G_OBJECT_TYPE_NAME (indenter), + peas_plugin_info_get_module_name (plugin_info)); + + // TODO: how to handle multiple indenters? + if (indenter == gtk_source_view_get_indenter (GTK_SOURCE_VIEW (self))) + gtk_source_view_set_indenter (GTK_SOURCE_VIEW (self), NULL); + + IDE_EXIT; +} + void _ide_source_view_addins_init (IdeSourceView *self, GtkSourceLanguage *language) @@ -195,6 +248,25 @@ _ide_source_view_addins_init (IdeSourceView *self, ide_source_view_hover_provider_added_cb, self); + /* Create our hover providers and attach them */ + self->indenters = + ide_extension_set_adapter_new (IDE_OBJECT (parent), + peas_engine_get_default (), + GTK_SOURCE_TYPE_INDENTER, + "Indenter-Languages", + language_id); + g_signal_connect (self->indenters, + "extension-added", + G_CALLBACK (ide_source_view_indenter_added_cb), + self); + g_signal_connect (self->indenters, + "extension-removed", + G_CALLBACK (ide_source_view_indenter_removed_cb), + self); + ide_extension_set_adapter_foreach (self->indenters, + ide_source_view_indenter_added_cb, + self); + IDE_EXIT; } @@ -207,6 +279,7 @@ _ide_source_view_addins_shutdown (IdeSourceView *self) ide_clear_and_destroy_object (&self->completion_providers); ide_clear_and_destroy_object (&self->hover_providers); + ide_clear_and_destroy_object (&self->indenters); IDE_EXIT; } @@ -223,6 +296,7 @@ _ide_source_view_addins_set_language (IdeSourceView *self, g_return_if_fail (!language || GTK_SOURCE_IS_LANGUAGE (language)); g_return_if_fail (self->completion_providers != NULL); g_return_if_fail (self->hover_providers != NULL); + g_return_if_fail (self->indenters != NULL); if (language != NULL) language_id = gtk_source_language_get_id (language); @@ -231,6 +305,7 @@ _ide_source_view_addins_set_language (IdeSourceView *self, ide_extension_set_adapter_set_value (self->completion_providers, language_id); ide_extension_set_adapter_set_value (self->hover_providers, language_id); + ide_extension_set_adapter_set_value (self->indenters, language_id); IDE_EXIT; } diff --git a/src/libide/sourceview/ide-source-view-private.h b/src/libide/sourceview/ide-source-view-private.h index 07cffe69b4a4f797dbf55344a35ae8328b50b5e3..aa33c12d4a5b1a860053d2c4dbf32aa2207323fb 100644 --- a/src/libide/sourceview/ide-source-view-private.h +++ b/src/libide/sourceview/ide-source-view-private.h @@ -61,6 +61,7 @@ struct _IdeSourceView */ IdeExtensionSetAdapter *completion_providers; IdeExtensionSetAdapter *hover_providers; + IdeExtensionSetAdapter *indenters; /* Mouse click position */ double click_x; diff --git a/src/libide/sourceview/ide-source-view.c b/src/libide/sourceview/ide-source-view.c index bfeab5a6f84826bf7c9ae535069fcbe8705f22e8..3fd77271c72afd60e37ed0fa88b5af9599011ced 100644 --- a/src/libide/sourceview/ide-source-view.c +++ b/src/libide/sourceview/ide-source-view.c @@ -671,6 +671,7 @@ ide_source_view_dispose (GObject *object) g_assert (self->completion_providers == NULL); g_assert (self->hover_providers == NULL); + g_assert (self->indenters == NULL); G_OBJECT_CLASS (ide_source_view_parent_class)->dispose (object); diff --git a/src/plugins/xml-pack/ide-xml-completion-provider.c b/src/plugins/xml-pack/ide-xml-completion-provider.c index c04b015e7bdcb52e001da736ab282d59c85d7b1f..74982cab3fdf28b00477daaad0a4c2323ac37c3a 100644 --- a/src/plugins/xml-pack/ide-xml-completion-provider.c +++ b/src/plugins/xml-pack/ide-xml-completion-provider.c @@ -1032,7 +1032,7 @@ ide_xml_completion_provider_populate_async (GtkSourceCompletionProvider *provide { IdeXmlCompletionProvider *self = (IdeXmlCompletionProvider *)provider; g_autoptr(IdeTask) task = NULL; - GtkTextBuffer *buffer; + GtkSourceBuffer *buffer; IdeXmlService *service; PopulateState *state; IdeContext *ide_context; @@ -1079,7 +1079,7 @@ ide_xml_completion_provider_populate_finish (GtkSourceCompletionProvider *provi return ide_task_propagate_object (IDE_TASK (result), error); } -static gboolean +static void ide_xml_completion_provider_refilter (GtkSourceCompletionProvider *provider, GtkSourceCompletionContext *context, GListModel *model) @@ -1109,8 +1109,6 @@ ide_xml_completion_provider_refilter (GtkSourceCompletionProvider *provider, if (!gtk_source_completion_fuzzy_match (label, casefold, &priority)) g_list_store_remove (G_LIST_STORE (model), i - 1); } - - return TRUE; } static void @@ -1120,19 +1118,30 @@ ide_xml_completion_provider_display (GtkSourceCompletionProvider *provider, GtkSourceCompletionCell *cell) { IdeXmlProposal *item = (IdeXmlProposal *)proposal; - const gchar *label; g_assert (IDE_IS_XML_COMPLETION_PROVIDER (provider)); - g_assert (GTK_SOURCE_IS_COMPLETION_LIST_BOX_ROW (row)); + g_assert (GTK_SOURCE_IS_COMPLETION_CELL (cell)); g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (context)); g_assert (GTK_SOURCE_IS_COMPLETION_PROPOSAL (proposal)); - label = ide_xml_proposal_get_label (item); + switch (gtk_source_completion_cell_get_column (cell)) + { + case GTK_SOURCE_COMPLETION_COLUMN_ICON: + gtk_source_completion_cell_set_icon_name (cell, "text-xml-symbolic"); + break; - gtk_source_completion_list_box_row_set_icon_name (row, NULL); - gtk_source_completion_list_box_row_set_left (row, NULL); - gtk_source_completion_list_box_row_set_right (row, NULL); - gtk_source_completion_list_box_row_set_center_markup (row, label); + case GTK_SOURCE_COMPLETION_COLUMN_TYPED_TEXT: + gtk_source_completion_cell_set_markup (cell, ide_xml_proposal_get_label (item)); + break; + + case GTK_SOURCE_COMPLETION_COLUMN_COMMENT: + case GTK_SOURCE_COMPLETION_COLUMN_DETAILS: + case GTK_SOURCE_COMPLETION_COLUMN_BEFORE: + case GTK_SOURCE_COMPLETION_COLUMN_AFTER: + default: + gtk_source_completion_cell_set_text (cell, NULL); + break; + } } static void @@ -1141,6 +1150,7 @@ ide_xml_completion_provider_activate (GtkSourceCompletionProvider *provider, GtkSourceCompletionProposal *proposal) { IdeXmlProposal *item = (IdeXmlProposal *)proposal; + GtkSourceBuffer *source_buffer; GtkTextBuffer *buffer; GtkTextIter begin, end; const gchar *text; @@ -1151,9 +1161,10 @@ ide_xml_completion_provider_activate (GtkSourceCompletionProvider *provider, text = ide_xml_proposal_get_text (item); - buffer = gtk_source_completion_context_get_buffer (context); + source_buffer = gtk_source_completion_context_get_buffer (context); gtk_source_completion_context_get_bounds (context, &begin, &end); + buffer = GTK_TEXT_BUFFER (source_buffer); gtk_text_buffer_begin_user_action (buffer); gtk_text_buffer_delete (buffer, &begin, &end); gtk_text_buffer_insert (buffer, &begin, text, -1); diff --git a/src/plugins/xml-pack/ide-xml-indenter.c b/src/plugins/xml-pack/ide-xml-indenter.c index f855a02939555b47245d619be841f772e2a5c889..10dda83b8e868f4e0266613c580326a37b42f055 100644 --- a/src/plugins/xml-pack/ide-xml-indenter.c +++ b/src/plugins/xml-pack/ide-xml-indenter.c @@ -28,9 +28,17 @@ #include "ide-xml-indenter.h" +typedef enum +{ + IDE_XML_INDENT_ACTION_INDENT_FORWARD, + IDE_XML_INDENT_ACTION_INDENT_BACKWARD, + IDE_XML_INDENT_ACTION_ADD_CLOSING_TAG, +} IdeXmlIndentAction; + struct _IdeXmlIndenter { IdeObject parent_class; + IdeXmlIndentAction indent_action; guint tab_width; guint indent_width; guint use_tabs : 1; @@ -194,23 +202,22 @@ cleanup: return ret; } -static gchar * -ide_xml_indenter_indent (IdeXmlIndenter *xml, - GtkTextIter *begin, - GtkTextIter *end, - gint *cursor_offset) +static void +ide_xml_indenter_indent_forward (IdeXmlIndenter *xml, + GtkTextIter *iter) { + g_autoptr(GString) str = NULL; + GtkTextBuffer *buffer; GtkTextIter match_begin; - GString *str; + gint backward_chars = 0; guint offset; - g_return_val_if_fail (IDE_IS_XML_INDENTER (xml), NULL); - g_return_val_if_fail (begin, NULL); - g_return_val_if_fail (end, NULL); + g_return_if_fail (IDE_IS_XML_INDENTER (xml)); + g_return_if_fail (iter); str = g_string_new (NULL); - if (text_iter_backward_to_element_start (begin, &match_begin)) + if (text_iter_backward_to_element_start (iter, &match_begin)) { offset = gtk_text_iter_get_line_offset (&match_begin); build_indent (xml, offset + xml->indent_width, &match_begin, str); @@ -219,10 +226,10 @@ ide_xml_indenter_indent (IdeXmlIndenter *xml, * If immediately after our cursor is a closing tag, we will move it to * a line after our indent line. */ - if ('<' == gtk_text_iter_get_char (end) && - '/' == text_iter_peek_next_char (end)) + if ('<' == gtk_text_iter_get_char (iter) && + '/' == text_iter_peek_next_char (iter)) { - GString *str2; + g_autoptr(GString) str2 = NULL; str2 = g_string_new (NULL); build_indent (xml, offset, &match_begin, str2); @@ -230,36 +237,34 @@ ide_xml_indenter_indent (IdeXmlIndenter *xml, g_string_append (str, "\n"); g_string_append (str, str2->str); - *cursor_offset = -str2->len - 1; - - g_string_free (str2, TRUE); + backward_chars = str2->len + 1; } - - IDE_GOTO (cleanup); } - /* do nothing */ + buffer = gtk_text_iter_get_buffer (iter); + gtk_text_buffer_insert (buffer, iter, str->str, str->len); -cleanup: - return g_string_free (str, (str->len == 0)); + if (backward_chars != 0) + { + gtk_text_iter_backward_chars (iter, backward_chars); + gtk_text_buffer_place_cursor (buffer, iter); + } } -static gchar * -ide_xml_indenter_maybe_unindent (IdeXmlIndenter *xml, - GtkTextIter *begin, - GtkTextIter *end) +static void +ide_xml_indenter_indent_backward (IdeXmlIndenter *xml, + GtkTextIter *iter) { GtkTextIter tmp; gunichar ch; - g_return_val_if_fail (IDE_IS_XML_INDENTER (xml), NULL); - g_return_val_if_fail (begin, NULL); - g_return_val_if_fail (end, NULL); + g_return_if_fail (IDE_IS_XML_INDENTER (xml)); + g_return_if_fail (iter); - tmp = *begin; + tmp = *iter; if (!gtk_text_iter_backward_char (&tmp)) - return NULL; + return; if (('/' == gtk_text_iter_get_char (&tmp)) && gtk_text_iter_backward_char (&tmp) && @@ -269,9 +274,12 @@ ide_xml_indenter_maybe_unindent (IdeXmlIndenter *xml, { if (ch == '\t') { + GtkTextBuffer *buffer = gtk_text_iter_get_buffer (iter); + gtk_text_iter_backward_char (&tmp); - *begin = tmp; - return g_strdup ("' || g_unichar_isspace (ch)); } -static gchar * -ide_xml_indenter_maybe_add_closing (IdeXmlIndenter *xml, - GtkTextIter *begin, - GtkTextIter *end, - gint *cursor_offset) +static void +ide_xml_indenter_add_closing_tag (IdeXmlIndenter *xml, + GtkTextIter *iter) { GtkTextIter match_begin; GtkTextIter match_end; GtkTextIter copy; - g_return_val_if_fail (IDE_IS_XML_INDENTER (xml), NULL); - g_return_val_if_fail (begin, NULL); - g_return_val_if_fail (end, NULL); + g_return_if_fail (IDE_IS_XML_INDENTER (xml)); + g_return_if_fail (iter); - copy = *begin; + copy = *iter; gtk_text_iter_backward_char (©); gtk_text_iter_backward_char (©); if (gtk_text_iter_get_char (©) == '/') - return NULL; + return; - copy = *begin; + copy = *iter; if (gtk_text_iter_backward_search (©, "<", GTK_TEXT_SEARCH_TEXT_ONLY, &match_begin, &match_end, NULL)) { @@ -337,49 +336,64 @@ ide_xml_indenter_maybe_add_closing (IdeXmlIndenter *xml, text = gtk_text_iter_get_slice (&match_begin, ©); if (strchr (text, '>')) - return NULL; + return; gtk_text_iter_forward_char (&match_begin); if (gtk_text_iter_get_char (&match_begin) == '/') - return NULL; + return; match_end = match_begin; - if (gtk_text_iter_forward_find_char (&match_end, find_end, NULL, begin)) + if (gtk_text_iter_forward_find_char (&match_end, find_end, NULL, iter)) { - gchar *slice; - gchar *ret = NULL; + g_autofree gchar *slice = NULL; slice = gtk_text_iter_get_slice (&match_begin, &match_end); if (slice && *slice && *slice != '!') { - if (gtk_text_iter_get_char (end) == '>') - ret = g_strdup_printf ("", slice); - *cursor_offset = -strlen (ret); - } + g_autoptr(GString) tag = NULL; + GtkTextBuffer *buffer; + + tag = g_string_new ("') + g_string_append_c (tag, '>'); - return ret; + buffer = gtk_text_iter_get_buffer (iter); + gtk_text_buffer_insert (buffer, iter, tag->str, tag->len); + gtk_text_iter_backward_chars (iter, tag->len); + gtk_text_buffer_place_cursor (buffer, iter); + } } } - - return NULL; } static gboolean ide_xml_indenter_is_trigger (GtkSourceIndenter *indenter, - GdkEventKey *event) + GtkSourceView *view, + const GtkTextIter *location, + GdkModifierType state, + guint keyval) { - switch (event->keyval) + IdeXmlIndenter *xml = (IdeXmlIndenter *)indenter; + + g_return_val_if_fail (IDE_IS_XML_INDENTER (xml), FALSE); + + switch (keyval) { case GDK_KEY_Return: case GDK_KEY_KP_Enter: + xml->indent_action = IDE_XML_INDENT_ACTION_INDENT_FORWARD; + return TRUE; + case GDK_KEY_slash: + xml->indent_action = IDE_XML_INDENT_ACTION_INDENT_BACKWARD; + return TRUE; + case GDK_KEY_greater: + xml->indent_action = IDE_XML_INDENT_ACTION_ADD_CLOSING_TAG; return TRUE; default: @@ -389,29 +403,21 @@ ide_xml_indenter_is_trigger (GtkSourceIndenter *indenter, return FALSE; } -static gchar * -ide_xml_indenter_format (GtkSourceIndenter *indenter, - GtkTextView *view, - GtkTextIter *begin, - GtkTextIter *end, - gint *cursor_offset, - GdkEventKey *trigger) +static void +ide_xml_indenter_indent (GtkSourceIndenter *indenter, + GtkSourceView *view, + GtkTextIter *iter) { IdeXmlIndenter *xml = (IdeXmlIndenter *)indenter; guint tab_width = 2; gint indent_width = -1; - g_return_val_if_fail (IDE_IS_XML_INDENTER (xml), NULL); + g_return_if_fail (IDE_IS_XML_INDENTER (xml)); - *cursor_offset = 0; - - if (GTK_SOURCE_IS_VIEW (view)) - { - tab_width = gtk_source_view_get_tab_width (GTK_SOURCE_VIEW (view)); - indent_width = gtk_source_view_get_indent_width (GTK_SOURCE_VIEW (view)); - if (indent_width != -1) - tab_width = indent_width; - } + tab_width = gtk_source_view_get_tab_width (GTK_SOURCE_VIEW (view)); + indent_width = gtk_source_view_get_indent_width (GTK_SOURCE_VIEW (view)); + if (indent_width != -1) + tab_width = indent_width; xml->tab_width = tab_width; xml->use_tabs = !gtk_source_view_get_insert_spaces_instead_of_tabs (GTK_SOURCE_VIEW (view)); @@ -422,34 +428,34 @@ ide_xml_indenter_format (GtkSourceIndenter *indenter, xml->indent_width = indent_width; /* do nothing if we are in a cdata section */ - if (text_iter_in_cdata (begin)) - return NULL; + if (text_iter_in_cdata (iter)) + return; - switch (trigger->keyval) + switch (xml->indent_action) { - case GDK_KEY_Return: - case GDK_KEY_KP_Enter: - if ((trigger->state & GDK_SHIFT_MASK) == 0) - return ide_xml_indenter_indent (xml, begin, end, cursor_offset); - return NULL; + case IDE_XML_INDENT_ACTION_INDENT_FORWARD: + ide_xml_indenter_indent_forward (xml, iter); + break; - case GDK_KEY_slash: - return ide_xml_indenter_maybe_unindent (xml, begin, end); + case IDE_XML_INDENT_ACTION_INDENT_BACKWARD: + // FIXME: can't confirm it works since this is never triggered + ide_xml_indenter_indent_backward (xml, iter); + break; - case GDK_KEY_greater: - return ide_xml_indenter_maybe_add_closing (xml, begin, end, cursor_offset); + case IDE_XML_INDENT_ACTION_ADD_CLOSING_TAG: + // FIXME: can't confirm it works since this is never triggered + ide_xml_indenter_add_closing_tag (xml, iter); + break; default: - g_return_val_if_reached (NULL); + g_return_if_reached (); } - - return NULL; } static void indenter_iface_init (GtkSourceIndenterInterface *iface) { - iface->format = ide_xml_indenter_format; + iface->indent = ide_xml_indenter_indent; iface->is_trigger = ide_xml_indenter_is_trigger; } diff --git a/src/plugins/xml-pack/meson.build b/src/plugins/xml-pack/meson.build index 08986e341e73e9bb3fa5b25d0f4b6964515584b3..1137fdcaa5718b608bcac66d575eb2a726c4a52d 100644 --- a/src/plugins/xml-pack/meson.build +++ b/src/plugins/xml-pack/meson.build @@ -4,11 +4,11 @@ plugins_sources += files([ 'ide-xml-analysis.c', 'ide-xml-completion-attributes.c', 'ide-xml-completion-values.c', - # 'ide-xml-completion-provider.c', + 'ide-xml-completion-provider.c', 'ide-xml-diagnostic-provider.c', 'ide-xml-hash-table.c', 'ide-xml-highlighter.c', - # 'ide-xml-indenter.c', + 'ide-xml-indenter.c', 'ide-xml-parser.c', 'ide-xml-parser-generic.c', 'ide-xml-parser-ui.c', diff --git a/src/plugins/xml-pack/xml-pack-plugin.c b/src/plugins/xml-pack/xml-pack-plugin.c index 097c7aa1b5774cc509ab6238caed8f19030be560..435d5a77f544fec610e2a4faa9faf71b2114caa5 100644 --- a/src/plugins/xml-pack/xml-pack-plugin.c +++ b/src/plugins/xml-pack/xml-pack-plugin.c @@ -34,22 +34,18 @@ _IDE_EXTERN void _ide_xml_register_types (PeasObjectModule *module) { -#if 0 peas_object_module_register_extension_type (module, GTK_SOURCE_TYPE_COMPLETION_PROVIDER, IDE_TYPE_XML_COMPLETION_PROVIDER); -#endif peas_object_module_register_extension_type (module, IDE_TYPE_DIAGNOSTIC_PROVIDER, IDE_TYPE_XML_DIAGNOSTIC_PROVIDER); peas_object_module_register_extension_type (module, IDE_TYPE_HIGHLIGHTER, IDE_TYPE_XML_HIGHLIGHTER); -#if 0 peas_object_module_register_extension_type (module, GTK_SOURCE_TYPE_INDENTER, IDE_TYPE_XML_INDENTER); -#endif peas_object_module_register_extension_type (module, IDE_TYPE_SYMBOL_RESOLVER, IDE_TYPE_XML_SYMBOL_RESOLVER);