anjuta r4476 - in trunk: . plugins/document-manager plugins/search



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]