From 71944514e7d8837c624933f65bf716d9106dea88 Mon Sep 17 00:00:00 2001 From: Christian Persch Date: Wed, 7 Oct 2015 22:15:06 +0200 Subject: regex: Add API to check if some regexes match at an event position https://bugzilla.gnome.org/show_bug.cgi?id=741728 --- doc/reference/vte-sections.txt | 2 + src/vte.cc | 190 ++++++++++++++++++++++++++++++++++++++--- src/vte/vtedeprecated.h | 8 ++ src/vte/vteterminal.h | 6 ++ src/vteapp.c | 39 +++++++-- 5 files changed, 227 insertions(+), 18 deletions(-) diff --git a/doc/reference/vte-sections.txt b/doc/reference/vte-sections.txt index ecdaae34..6252fcd8 100644 --- a/doc/reference/vte-sections.txt +++ b/doc/reference/vte-sections.txt @@ -73,6 +73,7 @@ vte_terminal_search_get_regex vte_terminal_search_get_wrap_around vte_terminal_search_set_regex vte_terminal_search_set_wrap_around +vte_terminal_event_check_regex_simple vte_get_user_shell @@ -121,6 +122,7 @@ vte_terminal_match_set_cursor vte_terminal_match_add_gregex vte_terminal_search_get_gregex vte_terminal_search_set_gregex +vte_terminal_event_check_gregex_simple VteCharAttributes diff --git a/src/vte.cc b/src/vte.cc index f3b186fe..4ae96bbf 100644 --- a/src/vte.cc +++ b/src/vte.cc @@ -1624,7 +1624,8 @@ static gboolean match_check_pcre(VteTerminalPrivate *pvt, pcre2_match_data_8 *match_data, pcre2_match_context_8 *match_context, - struct vte_regex_and_flags *regex, + VteRegex *regex, + guint32 match_flags, gsize sattr, gsize eattr, gsize offset, @@ -1642,9 +1643,7 @@ match_check_pcre(VteTerminalPrivate *pvt, const char *line; int r = 0; - g_assert_cmpint(regex->mode, ==, VTE_REGEX_PCRE2); - - if (_vte_regex_get_jited(regex->pcre.regex)) + if (_vte_regex_get_jited(regex)) match_fn = pcre2_jit_match_8; else match_fn = pcre2_match_8; @@ -1661,10 +1660,10 @@ match_check_pcre(VteTerminalPrivate *pvt, */ position = sattr; while (position < eattr && - ((r = match_fn(vte_regex_get_pcre(regex->pcre.regex), + ((r = match_fn(vte_regex_get_pcre(regex), (PCRE2_SPTR8)line, line_length, /* subject, length */ position, /* start offset */ - regex->pcre.match_flags | + match_flags | PCRE2_NO_UTF_CHECK | PCRE2_NOTEMPTY | PCRE2_PARTIAL_SOFT /* FIXME: HARD? */, match_data, match_context)) >= 0 || r == PCRE2_ERROR_PARTIAL)) { @@ -1779,9 +1778,12 @@ vte_terminal_match_check_internal_pcre(VteTerminal *terminal, continue; } + g_assert_cmpint(regex->regex.mode, ==, VTE_REGEX_PCRE2); + if (match_check_pcre(terminal->pvt, match_data, match_context, - ®ex->regex, + regex->regex.pcre.regex, + regex->regex.pcre.match_flags, sattr, eattr, offset, &dingu_match, start, end, @@ -1831,7 +1833,8 @@ vte_terminal_match_check_internal_pcre(VteTerminal *terminal, static gboolean match_check_gregex(VteTerminalPrivate *pvt, - struct vte_regex_and_flags *regex, + GRegex *regex, + GRegexMatchFlags match_flags, gsize sattr, gsize eattr, gsize offset, @@ -1846,18 +1849,16 @@ match_check_gregex(VteTerminalPrivate *pvt, gsize line_length; gint sblank = G_MININT, eblank = G_MAXINT; - g_assert_cmpint(regex->mode, ==, VTE_REGEX_GREGEX); - line = pvt->match_contents; line_length = eattr; /* We'll only match the first item in the buffer which * matches, so we'll have to skip each match until we * stop getting matches. */ - if (!g_regex_match_full(regex->gregex.regex, + if (!g_regex_match_full(regex, line, line_length, /* subject, length */ sattr, /* start position */ - regex->gregex.match_flags, + match_flags, &match_info, NULL)) { g_match_info_free(match_info); @@ -1958,8 +1959,11 @@ vte_terminal_match_check_internal_gregex(VteTerminal *terminal, continue; } + g_assert_cmpint(regex->regex.mode, ==, VTE_REGEX_GREGEX); + if (match_check_gregex(terminal->pvt, - ®ex->regex, + regex->regex.gregex.regex, + regex->regex.gregex.match_flags, sattr, eattr, offset, &dingu_match, start, end, @@ -2163,6 +2167,166 @@ vte_terminal_match_check_event(VteTerminal *terminal, return vte_terminal_match_check(terminal, col, row, tag); } +/** + * vte_terminal_event_check_regex_simple: + * @terminal: a #VteTerminal + * @event: a #GdkEvent + * @regexes: (array length=n_regexes): an array of #VteRegex + * @n_regexes: number of items in @regexes + * @flags: PCRE2 match flags, or 0 + * @matches: (out caller-allocates) (array length=n_regexes): a location to store the matches + * + * Checks each regex in @regexes if the text in and around the position of + * the event matches the regular expressions. If a match exists, the matched + * text is stored in @matches at the position of the regex in @regexes; otherwise + * %NULL is stored there. + * + * Returns: %TRUE iff any of the regexes produced a match + * + * Since: 0.44 + */ +gboolean +vte_terminal_event_check_regex_simple(VteTerminal *terminal, + GdkEvent *event, + VteRegex **regexes, + gsize n_regexes, + guint32 match_flags, + char **matches) +{ + VteTerminalPrivate *pvt = terminal->pvt; + gsize offset, sattr, eattr; + pcre2_match_data_8 *match_data; + pcre2_match_context_8 *match_context; + gboolean any_matches = FALSE; + double x, y; + long col, row; + guint i; + + g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE); + g_return_val_if_fail(event != NULL, FALSE); + g_return_val_if_fail(regexes != NULL || n_regexes == 0, FALSE); + g_return_val_if_fail(matches != NULL, FALSE); + + if (((GdkEventAny*)event)->window != gtk_widget_get_window(&terminal->widget)) + return FALSE; + if (!gdk_event_get_coords(event, &x, &y)) + return FALSE; + if (!_vte_terminal_xy_to_grid(terminal, x, y, &col, &row)) + return FALSE; + + if (pvt->match_contents == NULL) { + vte_terminal_match_contents_refresh(terminal); + } + + if (!match_rowcol_to_offset(terminal, col, row, + &offset, &sattr, &eattr)) + return FALSE; + + match_context = create_match_context(); + match_data = pcre2_match_data_create_8(256 /* should be plenty */, NULL /* general context */); + + for (i = 0; i < n_regexes; i++) { + gsize start, end, sblank, eblank; + char *match; + + g_return_val_if_fail(regexes[i] != NULL, FALSE); + + if (match_check_pcre(terminal->pvt, + match_data, match_context, + regexes[i], match_flags, + sattr, eattr, offset, + &match, + &start, &end, + &sblank, &eblank)) { + _vte_debug_print(VTE_DEBUG_REGEX, "Matched regex with text: %s\n", match); + matches[i] = match; + any_matches = TRUE; + } else + matches[i] = NULL; + } + + pcre2_match_data_free_8(match_data); + pcre2_match_context_free_8(match_context); + + return any_matches; +} + +/** + * vte_terminal_event_check_gregex_simple: + * @terminal: a #VteTerminal + * @event: a #GdkEvent + * @regexes: (array length=n_regexes): an array of #GRegex + * @n_regexes: number of items in @regexes + * @gflags: the #GRegexMatchFlags to use when matching the regexes + * @matches: (out caller-allocates) (array length=n_regexes): a location to store the matches + * + * Checks each regex in @regexes if the text in and around the position of + * the event matches the regular expressions. If a match exists, the matched + * text is stored in @matches at the position of the regex in @regexes; otherwise + * %NULL is stored there. + * + * Returns: %TRUE iff any of the regexes produced a match + * + * Since: 0.44 + * Deprecated: 0.44: Use vte_terminal_event_check_regex_simple() instead. + */ +gboolean +vte_terminal_event_check_gregex_simple(VteTerminal *terminal, + GdkEvent *event, + GRegex **regexes, + gsize n_regexes, + GRegexMatchFlags match_flags, + char **matches) +{ + VteTerminalPrivate *pvt = terminal->pvt; + gsize offset, sattr, eattr; + gboolean any_matches = FALSE; + double x, y; + long col, row; + guint i; + + g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE); + g_return_val_if_fail(event != NULL, FALSE); + g_return_val_if_fail(regexes != NULL || n_regexes == 0, FALSE); + g_return_val_if_fail(matches != NULL, FALSE); + + if (((GdkEventAny*)event)->window != gtk_widget_get_window(&terminal->widget)) + return FALSE; + if (!gdk_event_get_coords(event, &x, &y)) + return FALSE; + if (!_vte_terminal_xy_to_grid(terminal, x, y, &col, &row)) + return FALSE; + + if (pvt->match_contents == NULL) { + vte_terminal_match_contents_refresh(terminal); + } + + if (!match_rowcol_to_offset(terminal, col, row, + &offset, &sattr, &eattr)) + return FALSE; + + for (i = 0; i < n_regexes; i++) { + gsize start, end, sblank, eblank; + char *match; + + g_return_val_if_fail(regexes[i] != NULL, FALSE); + + if (match_check_gregex(terminal->pvt, + regexes[i], match_flags, + sattr, eattr, offset, + &match, + &start, &end, + &sblank, &eblank)) { + _vte_debug_print(VTE_DEBUG_REGEX, "Matched gregex with text: %s\n", match); + matches[i] = match; + any_matches = TRUE; + } else + matches[i] = NULL; + } + + return any_matches; +} + /* Emit an adjustment changed signal on our adjustment object. */ static void vte_terminal_emit_adjustment_changed(VteTerminal *terminal) diff --git a/src/vte/vtedeprecated.h b/src/vte/vtedeprecated.h index fcd928f4..32f26841 100644 --- a/src/vte/vtedeprecated.h +++ b/src/vte/vtedeprecated.h @@ -46,6 +46,14 @@ char *vte_terminal_match_check(VteTerminal *terminal, glong column, glong row, int *tag) _VTE_GNUC_NONNULL(1) G_GNUC_MALLOC; +_VTE_DEPRECATED +gboolean vte_terminal_event_check_gregex_simple(VteTerminal *terminal, + GdkEvent *event, + GRegex **regexes, + gsize n_regexes, + GRegexMatchFlags match_flags, + char **matches) _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(2); + _VTE_DEPRECATED void vte_terminal_search_set_gregex (VteTerminal *terminal, GRegex *gregex, diff --git a/src/vte/vteterminal.h b/src/vte/vteterminal.h index 8f8a1e52..857819f3 100644 --- a/src/vte/vteterminal.h +++ b/src/vte/vteterminal.h @@ -310,6 +310,12 @@ void vte_terminal_match_remove_all(VteTerminal *terminal) _VTE_GNUC_NONNULL(1); char *vte_terminal_match_check_event(VteTerminal *terminal, GdkEvent *event, int *tag) _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(2) G_GNUC_MALLOC; +gboolean vte_terminal_event_check_regex_simple(VteTerminal *terminal, + GdkEvent *event, + VteRegex **regexes, + gsize n_regexes, + guint32 match_flags, + char **matches) _VTE_GNUC_NONNULL(1) _VTE_GNUC_NONNULL(2); void vte_terminal_search_set_regex (VteTerminal *terminal, VteRegex *regex, diff --git a/src/vteapp.c b/src/vteapp.c index 9931b5f1..bfddf8cc 100644 --- a/src/vteapp.c +++ b/src/vteapp.c @@ -48,6 +48,8 @@ static const char *builtin_dingus[] = { NULL }; +static gboolean use_gregex = FALSE; + static void window_title_changed(GtkWidget *widget, gpointer win) { @@ -145,6 +147,8 @@ button_pressed(GtkWidget *widget, GdkEventButton *event, gpointer data) char *match; int tag; GtkBorder padding; + gboolean has_extra_match; + char *extra_match; switch (event->button) { case 3: @@ -163,6 +167,32 @@ button_pressed(GtkWidget *widget, GdkEventButton *event, gpointer data) vte_terminal_match_remove(terminal, tag); } } +#ifdef WITH_PCRE2 + if (!use_gregex) { + VteRegex *regex = vte_regex_new("\\d+", -1, PCRE2_UTF, NULL); + has_extra_match = vte_terminal_event_check_regex_simple(terminal, + (GdkEvent*)event, + ®ex, 1, + 0, + &extra_match); + vte_regex_unref(regex); + } else +#endif + { + GRegex *regex = g_regex_new("\\d+", 0, 0, NULL); + has_extra_match = vte_terminal_event_check_gregex_simple(terminal, + (GdkEvent*)event, + ®ex, 1, + 0, + &extra_match); + g_regex_unref(regex); + } + + if (has_extra_match) + g_print("Extra regex match: %s\n", extra_match); + else + g_print("Extra regex didn't match\n"); + g_free(extra_match); break; case 1: case 2: @@ -525,8 +555,7 @@ parse_color (const gchar *value, static void add_dingus (VteTerminal *terminal, - char **dingus, - gboolean use_gregex) + char **dingus) { const GdkCursorType cursors[] = { GDK_GUMBY, GDK_HAND1 }; int id, i; @@ -591,7 +620,7 @@ main(int argc, char **argv) console = FALSE, keep = FALSE, icon_title = FALSE, shell = TRUE, reverse = FALSE, use_geometry_hints = TRUE, - use_scrolled_window = FALSE, use_gregex = FALSE, + use_scrolled_window = FALSE, show_object_notifications = FALSE, rewrap = TRUE; char *geometry = NULL; gint lines = -1; @@ -1006,10 +1035,10 @@ main(int argc, char **argv) /* Match "abcdefg". */ if (!no_builtin_dingus) { - add_dingus (terminal, (char **) builtin_dingus, use_gregex); + add_dingus (terminal, (char **) builtin_dingus); } if (dingus) { - add_dingus (terminal, dingus, use_gregex); + add_dingus (terminal, dingus); g_strfreev (dingus); } -- cgit v1.2.1