Ticket #4165 (new enhancement)

Opened 7 months ago

Last modified 5 months ago

[patch] MultiSearch – an AND-conjugated dynamic filtering of any listbox

Reported by: psprint Owned by:
Priority: major Milestone: Future Releases
Component: mc-core Version: master
Keywords: listbox,multi_search Cc:
Blocked By: Blocking:
Branch state: no branch Votes for changeset:

Description

Hi.
This time coding style is OK :) I submit a patch that:

  1. Adds a new command MultiSearch? which, when invoked:on any listbox:
  • adds an input field below the listbox (either shrinks the list or extends the dialog, if there is enough room),
  • waits for any input,
  • after a character will be entered, it filters the listbox with this string,
  • the string is split on spaces and matched one by one, case insensitively,
  • all keywords must match for an acceptance of a result.

It also adds a new option multi_search_active_by_default which causes all list boxes to be started with MultiSearch? active. For example, the window list allowing to quickly filter and choose :) Or the file history, syntax chooser, etc.

Attachments

0001-Add-MultiSearch-command-a-multi-term-AND-conjugated-.patch (37.3 KB) - added by psprint 7 months ago.
0001-Add-MultiSearch-command-a-multi-term-AND-conjugated-v2.patch (66.7 KB) - added by psprint 7 months ago.
make indent
0001-Add-MultiSearch-command-a-multi-term-AND-conjugated-v2.2.patch (34.1 KB) - added by psprint 7 months ago.
make indent
0001-MultiSearch-an-AND-chained-filtering-of-any-listbox_V3.patch (88.2 KB) - added by psprint 7 months ago.
sub-classing
MultiSearch_V4.1.patch (88.4 KB) - added by psprint 7 months ago.
MSG_INIT
MultiSearch_V4.3_final.patch (65.8 KB) - added by psprint 7 months ago.
MultiSearch_5.3-1_final.patch (72.9 KB) - added by psprint 6 months ago.
MultiSearch_v5.3-2_final.patch (75.9 KB) - added by psprint 6 months ago.
MultiSearch_v5.7.patch (79.0 KB) - added by psprint 5 months ago.
use options dialogs

Change History

comment:1 Changed 7 months ago by andrew_b

Changed 7 months ago by psprint

make indent

comment:2 Changed 7 months ago by psprint

I've added a patch after make indent.

Changed 7 months ago by psprint

make indent

comment:3 Changed 7 months ago by psprint

I've uploaded the correct file now.

comment:4 Changed 7 months ago by andrew_b

I think, if you want extend some class for special case, you should create a child class with extended capabilities. Not each input line is required to be assigned with some list box. In you case, the new child class of WInput should be created. Also, new class that extends WLisbox should be created too.

I urge you to read Hacking more carefully, not stopping at Indentation.


Some notes about your code.

+    l->list = g_steal_pointer (&l->list_keep);
+    g_auto (GStrv) query_terms = NULL;

g_steal_pointer() and g_auto() are introduced in GLib 2.44. MC requires at least 2.30.


+            /* Keys we want others to handle, if not forwarding them to a paired listbox */
+            if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
+                || parm == KEY_F (10) || parm == '\n')
+                return MSG_NOT_HANDLED;

Hardcoded keys should not be used.


+            send_message (WIDGET (in->forward_listbox), NULL, MSG_DRAW, 0, NULL);
+    send_message (l, NULL, MSG_DRAW, 0, NULL);
+    send_message (multi_search_in, NULL, MSG_DRAW, 0, NULL);
+        send_message (l, NULL, MSG_DRAW, 0, NULL);

widget_draw()


+    return ((WIDGET (l)->state & WST_FILTER) != 0);

widget_get_state()


+    in = WIDGET (WIDGET (l)->owner)->find_by_type (WIDGET (WIDGET (l)->owner), input_callback);

widget_find_by_type()


+    send_message (w, NULL, MSG_RESIZE, 0, &r_listbox);
+    send_message (owner, NULL, MSG_RESIZE, 0, &r_dialog);

widget_set_size_rect()


Last edited 7 months ago by andrew_b (previous) (diff)

comment:5 Changed 7 months ago by psprint

OK, I agree on the class extending opinion. I'll be working on the patch in the next few days. I have been working on other patch (the TAGS object selection lists) lately.

Other remarks:

  • as for the hardcoded keys – I've only left the existing code untouched; should I change it?
  • the widget_find_by_type() is IMO not a good choice – I think that the virtual function find_by_type() should be used as it is, as it allows the overloading and automatically directs the search to the most proper function.
Last edited 7 months ago by psprint (previous) (diff)

comment:6 Changed 7 months ago by psprint

I've created two classes:
WForwardingInput:

typedef struct
{
    WInput base;
    widget_cb_fn base_callback; /* Saved callback of  base class */

    /* Fields for new logic. */
    Widget *forward_to_widget;  /* The paired widget to receive unhandled keys */
} WForwardingInput;

– and WFilteringListbox:

typedef struct 
{
    WListbox base;
    widget_cb_fn base_callback; /* Saved callback of base class */

    /* Fields for new logic. */
    GQueue *list_keep;          /* Unfiltered list (used in  WST_FILTER state). */
    filt_listbox_resize_strategy_t resize_strategy;
} WFilteringListbox;

The two classes cooperate when paired to implement input-query equipped listbox. The pairing is initiated by the listbox which can create the input and extend/make room in dialog. I did run make indent and I've used the file templates.

Last edited 7 months ago by psprint (previous) (diff)

Changed 7 months ago by psprint

sub-classing

comment:7 Changed 7 months ago by andrew_b

This patch contains patch from #4174. Please cleanup.

If you want some refactoring before adding new feature or fixing bug, you should make refactoring in separated patch.

comment:8 Changed 7 months ago by psprint

Yes the string safety patch is included, I've forgot to mention. It would be difficult for me to separate the patch now. Could it stay as it is? As a general cleanup around the completion engine which is the main place where *prev_char are used.

comment:9 follow-up: ↓ 11 Changed 7 months ago by psprint

I've fixed all style issues, e.g.: replaced if (p) with if (p != NULL). Also:
– MSG_DRAW → widget_draw,
– I've added a MSG_INIT to WFilteringListbox where I conditionally (ini option) enable MultiSearch? on all listboxes; this way this repeated enabling snippet has been removed from various places where listboxes have been instantinated.

Changed 7 months ago by psprint

MSG_INIT

comment:10 Changed 7 months ago by ossi

It would be difficult for me to separate the patch now.

no, it wouldn't:

  • reset to pristine state
  • apply first patch
  • commit
  • overwrite with file state of second patch
  • commit

generally, you should familiarize yourself with git rebase -i and git gui (selective staging using the context menu). read https://sandofsky.com/workflow/git-workflow/ and maybe some "advanced git" tutorials.

comment:11 in reply to: ↑ 9 Changed 7 months ago by andrew_b

Replying to psprint:

I've fixed all style issues

No, you haven't.

, e.g.: replaced if (p) with if (p != NULL).

What about other pointers?
What about integers?

Please reread comment:4.

And I haven't talked in details about your implementation of new widgets yet.

Could you please show some screenshots of what you've done?

comment:12 Changed 7 months ago by psprint

Here's an Asciinema recording :)

PS. I've forgot to record an useful other feature – using of input's HistoryPrev? and Next commands. It adds another way of quickly moving across the various entries (files, functions, etc.) by repeating of previous searches… Here's a quick demo.

Last edited 7 months ago by psprint (previous) (diff)

comment:13 Changed 7 months ago by ossi

the features are undoubtedly cool, though some more polish seems in order:

  • the discoverability appears somewhat suboptimal. technically, it's not worse than ctrl-s in the panels, but it might be possible to do better here (where a bit more clutter would be acceptable)
  • somewhat related to that, the UI is rather jumpy. don't resize the window.
  • are the box drawing glitches in your code or is that just the recording program?

speaking of the panels, using for example ctrl-f (for 'filter') to implement the same there to complement/supersede ctrl-s would be quite awesome (i think there might be an issue open for it). then using the same hotkey in the listboxes would reduce the discoverability issue.

comment:14 follow-up: ↓ 15 Changed 7 months ago by psprint

The glitches are in the recording interface.

I think that resizing is needed because it feels more bad to miss an entry because of the list being shrunk than to observe the resize up. I've already made some steps however to limit the jumping, as I've too noticed the unpleasant effect that it has: asciicast. Some dialogs are resized up some other aren't and it also depends on the list size.

As for the discoverability, I agree – it would be best if there could be a visible UI element (some sort of a button) added to enable the search mode. The discoverability would then be 100%. What could it be, I wonder, however… Otherwise it would have to be enabled by default, IMO. I was having quiet hopes for this seeing how liberating the feature is.

I attach the hopefully final patch with all the requested changes done (coding style, no string patch, widget_find_by_type, widget_draw, etc. – only MSG_RESIZE is left unchanged as explained in patch).

Last edited 7 months ago by psprint (previous) (diff)

Changed 7 months ago by psprint

comment:15 in reply to: ↑ 14 Changed 7 months ago by andrew_b

Replying to psprint:

I attach the hopefully final patch with all the requested changes done (coding style, no string patch, widget_find_by_type, widget_draw, etc. – only MSG_RESIZE is left unchanged as explained in patch).

Sighed heavily...
Unfortunately, no.

// comments.
Integer variables that should be gboolean.
Non-explicit checks of pointers and integers.
Non-ASCII symbols.
Etc...

Why do I see all this but you don't?


No need in #include <glib-2.0/glib.h>.
<glib.h> is included in lib/global.h.

No need in

widget_cb_fn base_callback; /* Saved callback of base class */

Just make a callback of base class (WListbox) public. input_callback() is public already.

Is it possible to use MSG_NOTIFY instead of new message MSG_UPDATE_LIST?

+    /* Allocate memory for the object body. */
+    object = g_new (WForwardingInput, 1);
+
+    /* Forward the call to construct the inherited object. */
+    base = input_new (y, x, colors, len, text, histname, completion_flags);
+
+    /* Shallow copy and shallow release. */
+    object->base = *base;
+    g_free (base);

Sorry, but it's aufull.

Just create a <base_class>_init() function, make it public and use it in <base_class>_new() as well in <child_class>_new(). See widget_init() for reference.

Likewise for WFilteringListbox.

comment:16 Changed 6 months ago by psprint

This time I've really thoroughly checked the source and scanned for an improper style. Changes in this final patch:

– booleans instead of integers where needed,
– direct pointer checks == NULL / != NULL,
– introduced listbox_init() and input_init() functions and used them instead of calling *_new functions,
– updated a few comments and changed into /* … */ (I've only added few comments when quickly updating patch previously),
– used MSG_NOTIFY instead of MSG_UPDATE_LIST,
– extended input's initialization so that it has its history properly read also after later (not at creation) on-the-fly enabling of MultiSearch?,
– added pre_widget_unlink_cb typedef for the pre-unlink function (which unregisters the history events) instead of using generic GDestroyNotify,
– used only ascii symbols instead of the unicode glyphs such as … or · (with a little regrets as they add to the expressiveness of the comments; could this requirement be relaxed a little? to be allowed to use e.g.: ndash –, dot · and ellipsis … ?).

Changed 6 months ago by psprint

comment:17 Changed 6 months ago by psprint

I've added shorthand that I've mentioned earlier:
– if the query includes 'c' or 'h' word, then the listbox is being grepped for filenames ending with ".c" and ".h", respectively,
– if there aren't such files in general in the list, then the letters are grepped normally.

This allows to quickly narrow down the list of files to either source or header… like here :) Patch v5.3-2 is attached.

Changed 6 months ago by psprint

comment:18 Changed 5 months ago by psprint

Changes in v5.7:

  • to address the discoverability suggestions from ossi: add the option enabling MultiSearch on all listboxes to the main options dialogs in mc and mcedit
  • fix a segfault.

I'm constantly using MultiSearch on multiple listboxes (completion, window list, etc.) without problems for 1 month now.

Changed 5 months ago by psprint

use options dialogs

comment:19 Changed 5 months ago by andrew_b

I want you to understand one simple thought: while you hardcode file types in your patches (".c", "h", etc) your patches will not be accepted. No chance. At all.

Last edited 5 months ago by andrew_b (previous) (diff)

comment:20 Changed 5 months ago by ossi

afaict, what this file extension magic does is saving the user from typing one dot per query (you type "h wid" instead of ".h wid"). that's somewhat underwhelming to put it that way. :D

qt creator's locator, similar to modern web browsers' address bars, supports keywords (single letters, for the most part) at the start of the query to select specific search functions. of course, these mechanism are configurable and/or plugin-based. in any case, this seems rather inappropriate for generic search widgets, and is definitely outside the scope of this issue.

Note: See TracTickets for help on using tickets.