After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 765635 - editor: add support for "Search and replace"
editor: add support for "Search and replace"
Status: RESOLVED FIXED
Product: gnome-builder
Classification: Other
Component: editor
unspecified
Other All
: Normal enhancement
: ---
Assigned To: GNOME Builder Maintainers
GNOME Builder Maintainers
Depends on:
Blocks:
 
 
Reported: 2016-04-26 19:30 UTC by Christian Stadelmann
Modified: 2016-07-07 13:35 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
editor-frame: Add search-and-replace to the search UI (24.82 KB, patch)
2016-07-01 17:32 UTC, Matthew Leeds
accepted-commit_now Details | Review
editor-frame: Add actions to make search-and-replace work (16.02 KB, patch)
2016-07-01 17:32 UTC, Matthew Leeds
needs-work Details | Review
editor-frame: Only enable replace/replace-all at appropriate times (7.88 KB, patch)
2016-07-01 17:32 UTC, Matthew Leeds
reviewed Details | Review
command-bar: Allow command bar actions to steal focus (1.56 KB, patch)
2016-07-01 17:32 UTC, Matthew Leeds
accepted-commit_now Details | Review
command-bar: Implement the 'c' option for vim substitute commands (6.79 KB, patch)
2016-07-01 17:32 UTC, Matthew Leeds
needs-work Details | Review

Description Christian Stadelmann 2016-04-26 19:30:02 UTC
gnome-builder currently has no GUI for replacing strings. This is needed.
The implementation in GEdit is quite good, you might want to reuse it.
Comment 1 Christian Hergert 2016-04-26 20:06:27 UTC
We have a GSoC student working on this over the summer.

https://wiki.gnome.org/Apps/Builder/SearchAndReplace
Comment 2 Matthew Leeds 2016-07-01 17:32:11 UTC
Created attachment 330735 [details] [review]
editor-frame: Add search-and-replace to the search UI

This commit implements Allan's designs for the search-and-replace UI,
which add a replace text entry field, Replace and Replace All buttons,
and search option check boxes to the existing search UI. Project-wide
replace has not been implemented yet, and the "Search selection" option
was not included because the current selection is the default search text.
Comment 3 Matthew Leeds 2016-07-01 17:32:15 UTC
Created attachment 330736 [details] [review]
editor-frame: Add actions to make search-and-replace work

This commit adds actions so the "Replace" and "Replace All" buttons
work, using the GtkSourceView API. It also adds actions to show or hide
the Replace widgets, to show or hide the search options, and to exit the
search, which are all buttons in the search box.
Comment 4 Matthew Leeds 2016-07-01 17:32:19 UTC
Created attachment 330737 [details] [review]
editor-frame: Only enable replace/replace-all at appropriate times

When the search entry field is empty, the replace and replace-all
actions don't make sense, so the buttons should be insensitive.
Similarly, if no search occurrence is selected, the replace action
doesn't make sense. This commit sets up callbacks to try to ensure the
buttons are only sensitive (clickable) at appropriate times.
Comment 5 Matthew Leeds 2016-07-01 17:32:22 UTC
Created attachment 330738 [details] [review]
command-bar: Allow command bar actions to steal focus

Currently, gb_command_bar_hide grabs focus on whatever was focused
before the command bar popped up, or on the workbench. But if a command
bar action causes another widget to appear, we want to leave that in
focus. For example, the substitute action with the 'c' option will have
that behavior (:%s/this/that/gc).
Comment 6 Matthew Leeds 2016-07-01 17:32:26 UTC
Created attachment 330739 [details] [review]
command-bar: Implement the 'c' option for vim substitute commands

In vim, if the 'c' option is specified at the end of a substitute
command (like :%s/this/that/gc), the user is prompted to confirm or deny
replacement on each occurrence of the search term. This commit makes
Builder recognize that option and respond by opening the replace UI with
the search entry and replace entry fields filled in.

Because the search_revealer animation doesn't happen immediately (it
waits for the frame clock to increment), we can't directly trigger the
"next-search-result" action in ide_editor_frame_actions_replace_confirm.
The selection would be cleared once the search_entry widget is mapped.
So instead we setup a callback on the buffer's has-selection property,
and use that to trigger next-search-result if there's a pending
replace-confirm action.
Comment 7 Christian Hergert 2016-07-05 21:50:32 UTC
Review of attachment 330738 [details] [review]:

LGTM
Comment 8 Christian Hergert 2016-07-05 22:01:25 UTC
Review of attachment 330739 [details] [review]:

In general looks good, few things to fix

::: libide/editor/ide-editor-frame-actions.c
@@ +365,3 @@
+{
+  IdeEditorFrame *self = user_data;
+  g_autofree const gchar **strv;

= NULL;

@@ +367,3 @@
+  g_autofree const gchar **strv;
+
+  g_assert (IDE_IS_EDITOR_FRAME (self));

Add additional assertions for:

 g_assert (state != NULL);
 g_assert (g_variant_is_of_type (state, G_VARIANT_TYPE_STRING_ARRAY));

@@ -361,0 +361,11 @@
+static void
+ide_editor_frame_actions_replace_confirm (GSimpleAction *action,
+                                          GVariant      *state,
... 8 more ...

Store the length of the array instead of NULL, and ensure length is >= 2 before dereferencing below.

::: libide/editor/ide-editor-frame-private.h
@@ -58,2 +58,3 @@
   guint                auto_hide_map : 1;
   guint                show_ruler : 1;
+  guint                pending_replace_confirm;

I tend to put whole/half word things together, so that we have one group of bitfields in the structure. So in this case, put pending_replace_confirm before the bitfields (next to the whole-word gulong field).

It's not a big deal here since we don't create a bunch of these structures, but it's good hygiene to practice.

::: plugins/command-bar/gb-vim.c
@@ +1074,3 @@
+            case 'c':
+              confirm_replace = TRUE;
+              break;

newline after break

@@ +1089,3 @@
 
+  if (confirm_replace)
+  {

We follow gtk+/gnu conventions, which indents the {} 2 spaces

@@ +1096,3 @@
+    g_variant_builder_add (builder, "s", search_text);
+    g_variant_builder_add (builder, "s", replace_text);
+    variant = g_variant_builder_end (builder);

This leaks. Calling g_variant_builder_end() is only enough if you are using a stack allocated builder. (Otherwise you still need to unref the builder too).

I'd just switch this to use a stack allocated builder and switch g_variant_builder_new() to g_variant_builder_init() and add appropriate &builder.
Comment 9 Christian Hergert 2016-07-05 22:05:36 UTC
Review of attachment 330737 [details] [review]:

The action setting reminds me that we should probably make a generic helper for this. In other classes we have a variadic helper that lets you do something like:

  action_set (self, "foo-action",
              "enabled", TRUE,
              NULL);

We could probably make that more generic and resolve the action from the widget hierarchy.

Either way, feel free to push after a couple small nits.

::: libide/editor/ide-editor-frame.c
@@ -407,0 +407,6 @@
+static void
+on_buffer_has_selection_changed (IdeEditorFrame *self,
+                                 GParamSpec     *pspec,
... 3 more ...

These declarations can probably be moved to the inner scope.

@@ +417,3 @@
+
+  if (!gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (buffer)))
+  {

Mind the indentation (2 spaces for {})
Comment 10 Christian Hergert 2016-07-05 23:16:29 UTC
Review of attachment 330735 [details] [review]:

LGTM
Comment 11 Christian Hergert 2016-07-05 23:20:48 UTC
Review of attachment 330736 [details] [review]:

Just a few things to fix

::: libide/editor/ide-editor-frame-actions.c
@@ -179,0 +180,10 @@
+static void
+ide_editor_frame_actions_toggle_search_replace (GSimpleAction *action,
+                                                GVariant      *state,
... 7 more ...

You might consider using gtk_widget_set_visible() here with the boolean value you determined for the branch. ½ the code

@@ -179,0 +180,36 @@
+static void
+ide_editor_frame_actions_toggle_search_replace (GSimpleAction *action,
+                                                GVariant      *state,
... 33 more ...

We don't use gtk_widget_show_all() in this code-base. Instead, toggle the visibility of the container (and leave the children always visible).

@@ -179,0 +180,78 @@
+static void
+ide_editor_frame_actions_toggle_search_replace (GSimpleAction *action,
+                                                GVariant      *state,
... 75 more ...

I think this is getting leaked?

@@ -179,0 +180,89 @@
+static void
+ide_editor_frame_actions_toggle_search_replace (GSimpleAction *action,
+                                                GVariant      *state,
... 86 more ...

Do keep in mind that g_return_if_fail() can be compiled out (generally we do not do that though). So if you intend for these to be more than "non-crashing assertions", they need to be changed.

@@ -179,0 +180,125 @@
+static void
+ide_editor_frame_actions_toggle_search_replace (GSimpleAction *action,
+                                                GVariant      *state,
... 122 more ...

Leaked here too

@@ -179,0 +180,144 @@
+static void
+ide_editor_frame_actions_toggle_search_replace (GSimpleAction *action,
+                                                GVariant      *state,
... 141 more ...

Potentially returning while completion is blocked. If we want the conditional checks, we need a `goto cleanup` or something.