anjuta r4476 - in trunk: . plugins/document-manager plugins/search
- From: jhs svn gnome org
- To: svn-commits-list gnome org
- Subject: anjuta r4476 - in trunk: . plugins/document-manager plugins/search
- Date: Sun, 21 Dec 2008 11:16:58 +0000 (UTC)
Author: jhs
Date: Sun Dec 21 11:16:57 2008
New Revision: 4476
URL: http://svn.gnome.org/viewvc/anjuta?rev=4476&view=rev
Log:
2008-12-18 Adam Dingle <adam medovina org>
Reviewed by: Johannes Schmid <jhs gnome org>
* plugins/document-manager/anjuta-document-manager.ui:
* plugins/document-manager/plugin.c:
Fixed #564987 â Save All, Close All should appear in Documents menu.
Added an icon to the Save All menu item.
* plugins/document-manager/plugin.c:
Fixed #565002 â Redo shortcut should be Ctrl+Shift+Z
* plugins/document-manager/plugin.c:
Fixed #565016 â Previous History and Next History should have shortcuts
* plugins/search/search-replace_backend.c
(file_buffer_line_from_pos), (file_match_line_from_pos),
(extra_match), (get_next_regex_match), (match_info),
(get_next_ascii_match), (normalize), (normal_advance),
(get_next_utf8_match), (str_is_ascii), (get_next_match):
* plugins/search/search-replace_backend.h:
Rewrote the UTF-8 search implementation to be faster and more robust.
When a search string is ASCII (which is virtually always the case),
we now bypass UTF-8 search altogether; this speeds up searching enormously.
Fixed bugs that sometimes caused incorrect lines to appear in the find output.
This fixes #563585 (Find in Files is extremely slow).
Modified:
trunk/ChangeLog
trunk/plugins/document-manager/anjuta-document-manager.ui
trunk/plugins/document-manager/plugin.c
trunk/plugins/search/search-replace_backend.c
trunk/plugins/search/search-replace_backend.h
Modified: trunk/plugins/document-manager/anjuta-document-manager.ui
==============================================================================
--- trunk/plugins/document-manager/anjuta-document-manager.ui (original)
+++ trunk/plugins/document-manager/anjuta-document-manager.ui Sun Dec 21 11:16:57 2008
@@ -5,9 +5,7 @@
<placeholder name="PlaceholderFileMenus">
<menuitem name="Save" action="ActionFileSave" />
<menuitem name="SaveAs" action="ActionFileSaveAs" />
- <menuitem name="SaveAll" action="ActionFileSaveAll" />
<menuitem name="Close" action="ActionFileClose" />
- <menuitem name="CloseAll" action="ActionFileCloseAll" />
<menuitem name="Reload" action="ActionFileReload" />
<separator name="separator4"/>
</placeholder>
@@ -104,6 +102,9 @@
</menu>
<placeholder name="PlaceHolderDocumentsMenus">
<menu name="Documents" action="ActionMenuDocuments">
+ <menuitem name="SaveAll" action="ActionFileSaveAll" />
+ <menuitem name="CloseAll" action="ActionFileCloseAll" />
+ <separator />
<menuitem name="PreviousDocument" action="ActionDocumentsPrevious" />
<menuitem name="NextDocument" action="ActionDocumentsNext" />
<separator />
Modified: trunk/plugins/document-manager/plugin.c
==============================================================================
--- trunk/plugins/document-manager/plugin.c (original)
+++ trunk/plugins/document-manager/plugin.c Sun Dec 21 11:16:57 2008
@@ -143,7 +143,7 @@
{ "ActionFileSaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), "<shift><control>s",
N_("Save the current file with a different name"),
G_CALLBACK (on_save_as_activate)},
- { "ActionFileSaveAll", NULL, N_("Save A_ll"), NULL,
+ { "ActionFileSaveAll", GTK_STOCK_SAVE, N_("Save A_ll"), "<shift><control>l",
N_("Save all currently open files, except new files"),
G_CALLBACK (on_save_all_activate)},
{ "ActionFileClose", GTK_STOCK_CLOSE, N_("_Close File"), "<control>w",
@@ -249,10 +249,10 @@
"<control><alt>e", N_("Go to the end of the current block"),
G_CALLBACK (on_goto_block_end1_activate)},
{ "ActionEditGotoHistoryPrev", ANJUTA_STOCK_HISTORY_PREV, N_("Previous _History"),
- NULL, N_("Goto previous history"),
+ "<alt>Left", N_("Goto previous history"),
G_CALLBACK (on_prev_history)},
{ "ActionEditGotoHistoryNext", ANJUTA_STOCK_HISTORY_NEXT, N_("Next Histor_y"),
- NULL, N_("Goto next history"),
+ "<alt>Right", N_("Goto next history"),
G_CALLBACK (on_next_history)}
};
@@ -284,7 +284,7 @@
{ "ActionEditUndo", GTK_STOCK_UNDO, N_("U_ndo"), "<control>z",
N_("Undo the last action"),
G_CALLBACK (on_editor_command_undo_activate)},
- { "ActionEditRedo", GTK_STOCK_REDO, N_("_Redo"), "<control>r",
+ { "ActionEditRedo", GTK_STOCK_REDO, N_("_Redo"), "<shift><control>z",
N_("Redo the last undone action"),
G_CALLBACK (on_editor_command_redo_activate)},
{ "ActionEditCut", GTK_STOCK_CUT, N_("C_ut"), "<control>x",
Modified: trunk/plugins/search/search-replace_backend.c
==============================================================================
--- trunk/plugins/search/search-replace_backend.c (original)
+++ trunk/plugins/search/search-replace_backend.c Sun Dec 21 11:16:57 2008
@@ -230,7 +230,7 @@
{
const AnjutaEncoding *encoding_used = NULL;
gchar* converted_text;
- guint converted_len;
+ gsize converted_len;
converted_text = anjuta_convert_to_utf8 (fb->buf,
fb->len,
&encoding_used,
@@ -297,9 +297,10 @@
g_return_val_if_fail(fb && pos >= 0, 1);
if (FB_FILE == fb->type)
{
+ gchar* p = g_utf8_offset_to_pointer(fb->buf, pos);
for (tmp = fb->lines; tmp; tmp = g_list_next(tmp))
{
- if (pos < GPOINTER_TO_INT(tmp->data))
+ if (p - fb->buf < GPOINTER_TO_INT(tmp->data))
return lineno;
++ lineno;
}
@@ -321,11 +322,13 @@
file_match_line_from_pos(FileBuffer *fb, int pos)
{
gint length=1;
+ int start;
gint i;
g_return_val_if_fail(fb && pos >= 0, NULL);
- for (i= pos+1; ((fb->buf[i] != '\n') && (fb->buf[i] != '\0')); i++, length++);
- for (i= pos-1; (fb->buf[i] != '\n') && (i >= 0); i--, length++);
+ start = g_utf8_offset_to_pointer(fb->buf, pos) - fb->buf;
+ for (i= start+1; ((fb->buf[i] != '\n') && (fb->buf[i] != '\0')); i++, length++);
+ for (i= start-1; (fb->buf[i] != '\n') && (i >= 0); i--, length++);
return g_strndup (fb->buf + i + 1, length);
}
@@ -403,27 +406,29 @@
}
static gboolean
-extra_match (FileBuffer *fb, gchar* begin, gchar* end, SearchExpression *s)
+extra_match (gboolean at_start, gchar* begin, gchar* end, SearchExpression *s)
{
gunichar b, e;
b = g_utf8_get_char (g_utf8_prev_char (begin));
e = g_utf8_get_char (end);
+ if (g_unichar_ismark(e))
+ return FALSE; /* matched character continues past match end */
if (s->whole_line)
- if ((fb->pos == 0 || b == '\n' || b == '\r') &&
+ if ((at_start || b == '\n' || b == '\r') &&
(e == '\0' || e == '\n' || e == '\r'))
return TRUE;
else
return FALSE;
else if (s->whole_word)
- if ((fb->pos ==0 || !isawordchar(b)) &&
+ if ((at_start || !isawordchar(b)) &&
(e=='\0' || !isawordchar(e)))
return TRUE;
else
return FALSE;
else if (s->word_start)
- if (fb->pos ==0 || !isawordchar(b))
+ if (at_start || !isawordchar(b))
return TRUE;
else
return FALSE;
@@ -431,201 +436,230 @@
return TRUE;
}
-/* Returns the next match in the passed buffer. The search expression should
- be pre-compiled. The returned pointer should be freed with match_info_free()
- when no longer required. */
-MatchInfo *
-get_next_match(FileBuffer *fb, SearchDirection direction, SearchExpression *s)
+static MatchInfo *
+get_next_regex_match(FileBuffer *fb, SearchDirection direction, SearchExpression *s)
{
MatchInfo *mi = NULL;
-
- g_return_val_if_fail(fb && s, NULL);
+ GMatchInfo* match_info;
- if (s->regex)
+ if (s->regex_info == NULL)
{
- GMatchInfo* match_info;
- if (s->regex_info == NULL)
+ GError* error = NULL;
+ GRegexCompileFlags compile_flags = 0;
+ GRegexMatchFlags match_flags = 0;
+
+ match_flags |= G_REGEX_MATCH_NOTEMPTY;
+ if (!s->match_case)
{
- GError* error = NULL;
- GRegexCompileFlags compile_flags = 0;
- GRegexMatchFlags match_flags = 0;
-
- match_flags |= G_REGEX_MATCH_NOTEMPTY;
- if (!s->match_case)
- {
- compile_flags |= G_REGEX_CASELESS;
- }
- if (!s->greedy)
- {
- compile_flags |= G_REGEX_UNGREEDY;
- }
- s->regex_info = g_regex_new (s->search_str, compile_flags,
- match_flags, &error);
- if (error)
- {
- anjuta_util_dialog_error (NULL, error->message);
- g_error_free(error);
- s->regex_info = NULL;
- return NULL;
- }
+ compile_flags |= G_REGEX_CASELESS;
}
+ if (!s->greedy)
+ {
+ compile_flags |= G_REGEX_UNGREEDY;
+ }
+ s->regex_info = g_regex_new (s->search_str, compile_flags,
+ match_flags, &error);
+ if (error)
+ {
+ anjuta_util_dialog_error (NULL, error->message);
+ g_error_free(error);
+ s->regex_info = NULL;
+ return NULL;
+ }
+ }
- g_regex_match_full (s->regex_info, fb->buf, fb->len,
- g_utf8_offset_to_pointer (fb->buf, fb->pos) - fb->buf,
- G_REGEX_MATCH_NOTEMPTY, &match_info, NULL);
-
- if (g_match_info_matches (match_info))
+ g_regex_match_full (s->regex_info, fb->buf, fb->len,
+ g_utf8_offset_to_pointer (fb->buf, fb->pos) - fb->buf,
+ G_REGEX_MATCH_NOTEMPTY, &match_info, NULL);
+
+ if (g_match_info_matches (match_info))
+ {
+ gint start;
+ gint end;
+ int i;
+ mi = g_new0(MatchInfo, 1);
+ if (g_match_info_fetch_pos (match_info, 0, &start, &end))
{
- gint start;
- gint end;
- int i;
- mi = g_new0(MatchInfo, 1);
- if (g_match_info_fetch_pos (match_info, 0, &start, &end))
- {
- DEBUG_PRINT ("Regex: %d %d", start, end);
- mi->pos = g_utf8_pointer_to_offset (fb->buf, fb->buf + start);
- mi->len = g_utf8_pointer_to_offset (fb->buf, fb->buf + end) - mi->pos;
- mi->line = file_buffer_line_from_pos(fb, mi->pos);
- }
- for (i = 1; i < g_match_info_get_match_count(match_info); i++) /* Captured subexpressions */
+ DEBUG_PRINT ("Regex: %d %d", start, end);
+ mi->pos = g_utf8_pointer_to_offset (fb->buf, fb->buf + start);
+ mi->len = g_utf8_pointer_to_offset (fb->buf, fb->buf + end) - mi->pos;
+ mi->line = file_buffer_line_from_pos(fb, mi->pos);
+ }
+ for (i = 1; i < g_match_info_get_match_count(match_info); i++) /* Captured subexpressions */
+ {
+ MatchSubStr *ms;
+ ms = g_new0(MatchSubStr, 1);
+ if (g_match_info_fetch_pos (match_info, i, &start, &end))
{
- MatchSubStr *ms;
- ms = g_new0(MatchSubStr, 1);
- if (g_match_info_fetch_pos (match_info, i, &start, &end))
- {
- ms->start = g_utf8_pointer_to_offset (fb->buf, fb->buf + start);
- ms->len = g_utf8_pointer_to_offset (fb->buf, fb->buf + end) - ms->start;
- }
- mi->subs = g_list_prepend(mi->subs, ms);
+ ms->start = g_utf8_pointer_to_offset (fb->buf, fb->buf + start);
+ ms->len = g_utf8_pointer_to_offset (fb->buf, fb->buf + end) - ms->start;
}
- mi->subs = g_list_reverse(mi->subs);
- fb->pos = g_utf8_pointer_to_offset (fb->buf, fb->buf + end);
+ mi->subs = g_list_prepend(mi->subs, ms);
}
+ mi->subs = g_list_reverse(mi->subs);
+ fb->pos = g_utf8_pointer_to_offset (fb->buf, fb->buf + end);
+ }
+ return mi;
+}
+
+static MatchInfo *match_info(FileBuffer *fb, gchar* match, gchar* match_end,
+ SearchDirection direction)
+{
+ MatchInfo *mi = g_new0 (MatchInfo, 1);
+ mi->pos = g_utf8_pointer_to_offset(fb->buf, match);
+ mi->len = g_utf8_pointer_to_offset(match, match_end);
+ mi->line = file_buffer_line_from_pos (fb, mi->pos);
+
+ if (direction == SD_BACKWARD)
+ fb->pos = mi->pos;
+ else
+ fb->pos = mi->pos + mi->len;
+
+ return mi;
+}
+
+/* Get the next match, given that the search string is ASCII (as will nearly
+ * always be the case). In this case we don't need to perform any UTF-8
+ * normalization, since any bytes with values 0-127 in a UTF-8 string always
+ * actually represent ASCII characters. */
+static MatchInfo *
+get_next_ascii_match(FileBuffer *fb, SearchDirection direction, SearchExpression *s)
+{
+ int len = strlen(s->search_str);
+ gint (*compare)(const gchar *, const gchar *, gsize) =
+ s->match_case ? strncmp : g_ascii_strncasecmp;
+ gchar *p = g_utf8_offset_to_pointer (fb->buf, fb->pos);
+
+ if (direction == SD_BACKWARD)
+ {
+ for (p -= len; p >= fb->buf; --p)
+ if (!compare(p, s->search_str, len) &&
+ extra_match(p == fb->buf, p, p + len, s))
+ return match_info(fb, p, p + len, direction);
}
else
{
- /* Simple string search - this needs to be performance-tuned */
- gboolean found;
- gint match_len;
+ for (; *p ; ++p)
+ if (!compare(p, s->search_str, len) &&
+ extra_match(p == fb->buf, p, p + len, s))
+ return match_info(fb, p, p + len, direction);
+ }
+
+ return NULL;
+}
- match_len = g_utf8_strlen (s->search_str, -1);
- if (match_len == 0)
- return NULL;
+/* Normalize a UTF-8 string, optionally folding case. Returns NULL if invalid. */
+static gchar* normalize(gchar *s, int len_bytes, gboolean match_case) {
+ gchar *c, *n;
+
+ if (match_case)
+ return g_utf8_normalize(s, len_bytes, G_NORMALIZE_NFD);
+
+ c = g_utf8_casefold(s, len_bytes);
+ n = g_utf8_normalize(c, -1, G_NORMALIZE_NFD);
+ g_free(c);
+ return n;
+}
- found = FALSE;
- if (SD_BACKWARD == direction)
+/* Given a UTF-8 string s, advance past a prefix whose normalized length in
+ * bytes is [normal_bytes]. */
+static gchar* normal_advance(gchar *s, int normal_bytes, gboolean match_case)
+{
+ while (*s && normal_bytes > 0)
+ {
+ gchar *next = g_utf8_next_char(s);
+ gchar *n = normalize(s, next - s, match_case);
+ normal_bytes -= strlen(n);
+ g_free(n);
+ s = next;
+ }
+ return s;
+}
+
+static MatchInfo *
+get_next_utf8_match(FileBuffer *fb, SearchDirection direction, SearchExpression *s)
+{
+ gchar* search_key = normalize(s->search_str, -1, s->match_case);
+ gchar* current = g_utf8_offset_to_pointer (fb->buf, fb->pos);
+ gchar* search_buf;
+ gchar* p = NULL;
+ MatchInfo *mi = NULL;
+ int keylen;
+
+ if (!search_key)
+ return NULL;
+ keylen = strlen(search_key);
+
+ if (direction == SD_BACKWARD)
+ {
+ search_buf = normalize(fb->buf, current - fb->buf, s->match_case);
+ if (search_buf)
{
- /* Backward matching. */
- if (!s->match_case)
+ p = search_buf + strlen(search_buf);
+ while ((p = g_strrstr_len(search_buf, p - search_buf, search_key)) != NULL)
{
- gchar* current = g_utf8_offset_to_pointer (fb->buf, fb->pos);
- gint len = g_utf8_strlen (s->search_str, -1);
- gchar* search_caseless = g_utf8_casefold (s->search_str, len);
- for (; fb->pos >= len; --fb->pos)
- {
- gchar* current_caseless = g_utf8_casefold (current, len);
- if (g_utf8_collate (current_caseless, search_caseless) == 0 &&
- extra_match (fb, current, current + strlen (search_caseless),
- s))
- {
- found = TRUE;
- g_free (current_caseless);
- break;
- }
- else
- current = g_utf8_prev_char (current);
- }
- g_free (search_caseless);
- }
- else
- {
- gchar* current = g_utf8_offset_to_pointer (fb->buf, fb->pos);
- gint len = g_utf8_strlen (s->search_str, -1);
- gchar* search_key = g_utf8_collate_key (s->search_str, len);
- for (; fb->pos >= len; --fb->pos)
- {
- gchar* current_key = g_utf8_collate_key (current, len);
- if (g_str_equal (current_key, search_key) &&
- extra_match (fb, current, current + strlen (s->search_str),
- s))
- {
- found = TRUE;
- g_free (current_key);
- break;
- }
- else
- current = g_utf8_prev_char (current);
- }
- g_free (search_key);
+ if (extra_match (p == search_buf, p, p + keylen, s))
+ break;
+ p = p + keylen - 1;
}
}
- else
+ }
+ else
+ { /* forward search */
+ search_buf = normalize(current, -1, s->match_case);
+ if (search_buf)
{
- /* Forward match */
- if (!s->match_case)
- {
- gchar* current = g_utf8_offset_to_pointer (fb->buf, fb->pos);
- gint len = g_utf8_strlen (s->search_str, -1);
- gchar* search_caseless = g_utf8_casefold (s->search_str, len);
- gint buf_len = g_utf8_strlen (fb->buf, fb->len);
- for (; fb->pos < buf_len; ++fb->pos)
- {
- gchar* current_caseless = g_utf8_casefold (current, len);
- if (g_utf8_collate (current_caseless, search_caseless) == 0 &&
- extra_match (fb, current, current + strlen (search_caseless),
- s))
- {
- found = TRUE;
- g_free (current_caseless);
- break;
- }
- else
- current = g_utf8_next_char (current);
- }
- g_free (search_caseless);
- }
- else
+ p = search_buf;
+ while ((p = strstr(p, search_key)) != NULL)
{
- gchar* current = g_utf8_offset_to_pointer (fb->buf, fb->pos);
- gint len = g_utf8_strlen (s->search_str, -1);
- gint buf_len = g_utf8_strlen (fb->buf, fb->len);
- gchar* search_key = g_utf8_collate_key (s->search_str, len);
- for (; fb->pos < buf_len; ++fb->pos)
- {
- gchar* current_key = g_utf8_collate_key (current, len);
- if (g_str_equal (current_key, search_key) &&
- extra_match (fb, current, current + strlen (s->search_str),
- s))
- {
- found = TRUE;
- g_free (current_key);
- break;
- }
- else
- {
- g_free (current_key);
- current = g_utf8_next_char (current);
- }
- }
- g_free (search_key);
+ if (extra_match (fb->pos == 0 && p == search_buf, p, p + keylen, s))
+ break;
+ ++p;
}
}
- if (found)
- {
- mi = g_new0 (MatchInfo, 1); //better to abort than silently fail to report match ?
- mi->pos = fb->pos;
- mi->len = match_len;
- mi->line = file_buffer_line_from_pos (fb, fb->pos);
-
- if (direction == SD_BACKWARD)
- fb->pos -= match_len;
- else
- fb->pos += match_len;
- }
}
+ g_free(search_key);
+
+ if (p)
+ {
+ /* We've found a match in the normalized buffer. Now find the
+ * corresponding position in the source buffer. */
+ gchar* match = normal_advance(
+ direction == SD_BACKWARD ? fb->buf : current,
+ p - search_buf, s->match_case);
+ gchar* match_end = normal_advance(match, keylen, s->match_case);
+ mi = match_info(fb, match, match_end, direction);
+ }
+
+ g_free(search_buf);
return mi;
}
+static gboolean str_is_ascii(gchar* s)
+{
+ for (; *s; ++s)
+ if (!isascii(*s))
+ return FALSE;
+ return TRUE;
+}
+
+/* Returns the next match in the passed buffer. If direction is SD_BACKWARD,
+ finds the previous match whose end falls before the current position.
+ The search expression should be pre-compiled. The returned pointer should
+ be freed with match_info_free() when no longer required. */
+MatchInfo *
+get_next_match(FileBuffer *fb, SearchDirection direction, SearchExpression *s)
+{
+ g_return_val_if_fail(fb && s, NULL);
+
+ if (s->regex)
+ return get_next_regex_match(fb, direction, s);
+
+ return str_is_ascii(s->search_str) ?
+ get_next_ascii_match(fb, direction, s) :
+ get_next_utf8_match(fb, direction, s);
+}
+
/* Create list of search entries */
GList *
create_search_entries (Search *s)
Modified: trunk/plugins/search/search-replace_backend.h
==============================================================================
--- trunk/plugins/search/search-replace_backend.h (original)
+++ trunk/plugins/search/search-replace_backend.h Sun Dec 21 11:16:57 2008
@@ -142,16 +142,24 @@
typedef struct _FileBuffer
{
FileBufferType type;
+
/* The following are valid only for files loaded from disk */
gchar *name; /* Name of the file */
+
gchar *path; /* Full path to the file */
gchar *uri; /* URI to the file */
- gchar *buf; /* Contents of the file */
+ gchar *buf; /* Contents of the file, null-terminated */
gint len; /* Length of the buffer */
- gint pos; /* Current position */
- gint endpos; /* Restrict action upto this position */
+
+ /* Current position: a count of UTF-8 characters, *not* bytes. */
+ gint pos;
+
gint line; /* Current line */
- GList *lines; /* List of integers specifying line start positions */
+ GList *lines; /* List of integers specifying line start positions in bytes */
+
+ gchar *canonical; /* buffer text converted to canonical utf-8 form */
+ gchar *canonical_p; /* current pointer into canonical text */
+
/* The following are valid only for files corresponding to a TextEditor */
IAnjutaEditor *te;
} FileBuffer;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]