[geany/geany] 3443e2: Add flag to tm_tags_find() to indicate the tags array may not be sorted

Enrico Tröger git-noreply at xxxxx
Sun Oct 13 16:52:56 UTC 2013


Branch:      refs/heads/master
Author:      Enrico Tröger <enrico.troeger at uvena.de>
Committer:   Enrico Tröger <enrico.troeger at uvena.de>
Date:        Sun, 13 Oct 2013 16:52:56 UTC
Commit:      3443e288fe608306606f6b6a846886a377e46aff
             https://github.com/geany/geany/commit/3443e288fe608306606f6b6a846886a377e46aff

Log Message:
-----------
Add flag to tm_tags_find() to indicate the tags array may not be sorted

tm_tags_find() relies on a sorted tags array to be passed in but in
tm_source_file_set_tag_arglist() we don't have a sorted array yet and
sorting it on demand seems more heavy than the alternative:
make tm_tags_find() search the array linear if the new flag is set.

This fixes a bug in the Python parser when assigning the argument list
of __init__() methods to their class' argument list which annoyed me
for years already.

Also add a test case for this.


Modified Paths:
--------------
    tagmanager/src/tm_source_file.c
    tagmanager/src/tm_tag.c
    tagmanager/src/tm_tag.h
    tagmanager/src/tm_workspace.c
    tests/ctags/Makefile.am
    tests/ctags/py_constructor_arglist.py
    tests/ctags/py_constructor_arglist.py.tags

Modified: tagmanager/src/tm_source_file.c
3 files changed, 2 insertions(+), 1 deletions(-)
===================================================================
@@ -244,7 +244,8 @@ void tm_source_file_set_tag_arglist(const char *tag_name, const char *arglist)
 		return;
 	}
 
-	tags = tm_tags_find(current_source_file->work_object.tags_array, tag_name, FALSE, &count);
+	tags = tm_tags_find(current_source_file->work_object.tags_array, tag_name, FALSE, FALSE,
+			&count);
 	if (tags != NULL && count == 1)
 	{
 		tag = tags[0];


Modified: tagmanager/src/tm_tag.c
37 files changed, 30 insertions(+), 7 deletions(-)
===================================================================
@@ -878,14 +878,37 @@ void tm_tags_array_free(GPtrArray *tags_array, gboolean free_all)
 	}
 }
 
-TMTag **tm_tags_find(const GPtrArray *sorted_tags_array, const char *name,
-		gboolean partial, int * tagCount)
+static TMTag **tags_search(const GPtrArray *tags_array, TMTag *tag, gboolean partial,
+		gboolean tags_array_sorted)
+{
+	if (tags_array_sorted)
+	{	/* fast binary search on sorted tags array */
+		return (TMTag **) bsearch(&tag, tags_array->pdata, tags_array->len
+		  , sizeof(gpointer), tm_tag_compare);
+	}
+	else
+	{	/* the slow way: linear search (to make it a bit faster, search reverse assuming
+		 * that the tag to search was added recently) */
+		int i;
+		TMTag **t;
+		for (i = tags_array->len - 1; i >= 0; i--)
+		{
+			t = (TMTag **) &tags_array->pdata[i];
+			if (0 == tm_tag_compare(&tag, t))
+				return t;
+		}
+	}
+	return NULL;
+}
+
+TMTag **tm_tags_find(const GPtrArray *tags_array, const char *name,
+		gboolean partial, gboolean tags_array_sorted, int * tagCount)
 {
 	static TMTag *tag = NULL;
 	TMTag **result;
 	int tagMatches=0;
 
-	if ((!sorted_tags_array) || (!sorted_tags_array->len))
+	if ((!tags_array) || (!tags_array->len))
 		return NULL;
 
 	if (NULL == tag)
@@ -893,12 +916,12 @@ TMTag **tm_tags_find(const GPtrArray *sorted_tags_array, const char *name,
 	tag->name = (char *) name;
 	s_sort_attrs = NULL;
 	s_partial = partial;
-	result = (TMTag **) bsearch(&tag, sorted_tags_array->pdata, sorted_tags_array->len
-	  , sizeof(gpointer), tm_tag_compare);
+
+	result = tags_search(tags_array, tag, partial, tags_array_sorted);
 	/* There can be matches on both sides of result */
 	if (result)
 	{
-		TMTag **last = (TMTag **) &sorted_tags_array->pdata[sorted_tags_array->len - 1];
+		TMTag **last = (TMTag **) &tags_array->pdata[tags_array->len - 1];
 		TMTag **adv;
 
 		/* First look for any matches after result */
@@ -911,7 +934,7 @@ TMTag **tm_tags_find(const GPtrArray *sorted_tags_array, const char *name,
 			++tagMatches;
 		}
 		/* Now look for matches from result and below */
-		for (; result >= (TMTag **) sorted_tags_array->pdata; -- result)
+		for (; result >= (TMTag **) tags_array->pdata; -- result)
 		{
 			if (0 != tm_tag_compare(&tag, (TMTag **) result))
 				break;


Modified: tagmanager/src/tm_tag.h
12 files changed, 8 insertions(+), 4 deletions(-)
===================================================================
@@ -305,14 +305,18 @@ gboolean tm_tags_merge(GPtrArray *tags_array, gsize orig_len,
 gboolean tm_tags_custom_dedup(GPtrArray *tags_array, TMTagCompareFunc compare_func);
 
 /*!
- Returns a pointer to the position of the first matching tag in a sorted tags array.
- \param sorted_tags_array Tag array sorted on name
+ Returns a pointer to the position of the first matching tag in a (sorted) tags array.
+ The passed array of tags should be already sorted by name for optimal performance. If
+ \c tags_array_sorted is set to FALSE, it may be unsorted but the lookup will be slower.
+ \param tags_array Tag array (may be sorted on name)
  \param name Name of the tag to locate.
  \param partial If TRUE, matches the first part of the name instead of doing exact match.
+ \param tags_array_sorted If TRUE, the passed \c tags_array is sorted by name so it can be
+ searched with binary search. Otherwise it is searched linear which is obviously slower.
  \param tagCount Return location of the matched tags.
 */
-TMTag **tm_tags_find(const GPtrArray *sorted_tags_array, const char *name,
-		gboolean partial, int * tagCount);
+TMTag **tm_tags_find(const GPtrArray *tags_array, const char *name,
+		gboolean partial, gboolean tags_array_sorted, int * tagCount);
 
 /*!
  Completely frees an array of tags.


Modified: tagmanager/src/tm_workspace.c
7 files changed, 4 insertions(+), 3 deletions(-)
===================================================================
@@ -594,8 +594,9 @@ const GPtrArray *tm_workspace_find(const char *name, int type, TMTagAttrType *at
 	else
 		tags = g_ptr_array_new();
 
-	matches[0] = tm_tags_find(theWorkspace->work_object.tags_array, name, partial, &tagCount[0]);
-	matches[1] = tm_tags_find(theWorkspace->global_tags, name, partial, &tagCount[1]);
+	matches[0] = tm_tags_find(theWorkspace->work_object.tags_array, name, partial, TRUE,
+					&tagCount[0]);
+	matches[1] = tm_tags_find(theWorkspace->global_tags, name, partial, TRUE, &tagCount[1]);
 
 	/* file tags */
 	if (matches[0] && *matches[0])
@@ -690,7 +691,7 @@ static gboolean match_langs(gint lang, const TMTag *tag)
 	if ((!src) || (!dst) || (!name) || (!*name))
 		return 0;
 
-	match = tm_tags_find (src, name, partial, &count);
+	match = tm_tags_find (src, name, partial, TRUE, &count);
 	if (count && match && *match)
 	{
 		for (tagIter = 0; tagIter < count; ++tagIter)


Modified: tests/ctags/Makefile.am
1 files changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -194,6 +194,7 @@ test_sources = \
 	property.cs						\
 	prototype.h						\
 	pure_elem.f95					\
+	py_constructor_arglist.py		\
 	random.sql						\
 	readlob.sql						\
 	readlong.sql					\


Modified: tests/ctags/py_constructor_arglist.py
28 files changed, 28 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,28 @@
+"""
+Test arglist parsing of a class' __init__ method:
+the __init__() method's argument list should be assigned to the class' argument list.
+
+This is somewhat special to Python and the Python parses uses tm_source_file_set_tag_arglist()
+to do this and tm_source_file_set_tag_arglist() uses tm_tags_find() which by default relies on
+a sorted tags array. However, when the parses uses tm_source_file_set_tag_arglist() the tags
+array is *not yet* sorted and so it might be the tag for the class SomeClass can't be found,
+hence this test.
+"""
+
+from bsddb import btopen
+import sys
+from django.contrib.auth.decorators import login_required, permission_required
+from django.http import HttpResponse, HttpResponseBadRequest
+from django.shortcuts import render_to_response
+from django.template.context import RequestContext
+from django.utils import simplejson
+from django.views.decorators.csrf import csrf_exempt
+from django.views.decorators.csrf import csrf_exempt2
+from django.views.decorators.http import require_POST
+from vnstat.api.error import InterfaceDataValidationError
+
+
+class SomeClass(object):
+
+    def __init__(self, filename, pathsep='', treegap=64):
+        pass


Modified: tests/ctags/py_constructor_arglist.py.tags
16 files changed, 16 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,16 @@
+# format=tagmanager
+HttpResponse�256�0
+HttpResponseBadRequest�256�0
+InterfaceDataValidationError�256�0
+RequestContext�256�0
+SomeClass�1�(self, filename, pathsep='', treegap=64)�0
+__init__�128�(self, filename, pathsep='', treegap=64)�SomeClass�0
+btopen�256�0
+csrf_exempt�256�0
+csrf_exempt2�256�0
+login_required�256�0
+permission_required�256�0
+render_to_response�256�0
+require_POST�256�0
+simplejson�256�0
+sys�256�0



--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).


More information about the Commits mailing list