Skip to content

Commit

Permalink
Add flag to tm_tags_find() to indicate the tags array may not be sorted
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
eht16 committed Oct 13, 2013
1 parent a408938 commit 3443e28
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 15 deletions.
3 changes: 2 additions & 1 deletion tagmanager/src/tm_source_file.c
Expand Up @@ -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];
Expand Down
37 changes: 30 additions & 7 deletions tagmanager/src/tm_tag.c
Expand Up @@ -878,27 +878,50 @@ 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)
tag = g_new0(TMTag, 1);
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 */
Expand All @@ -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;
Expand Down
12 changes: 8 additions & 4 deletions tagmanager/src/tm_tag.h
Expand Up @@ -305,14 +305,18 @@ gboolean tm_tags_dedup(GPtrArray *tags_array, TMTagAttrType *sort_attributes);
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.
Expand Down
7 changes: 4 additions & 3 deletions tagmanager/src/tm_workspace.c
Expand Up @@ -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])
Expand Down Expand Up @@ -690,7 +691,7 @@ fill_find_tags_array (GPtrArray *dst, const GPtrArray *src,
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)
Expand Down
1 change: 1 addition & 0 deletions tests/ctags/Makefile.am
Expand Up @@ -194,6 +194,7 @@ test_sources = \
property.cs \
prototype.h \
pure_elem.f95 \
py_constructor_arglist.py \
random.sql \
readlob.sql \
readlong.sql \
Expand Down
28 changes: 28 additions & 0 deletions tests/ctags/py_constructor_arglist.py
@@ -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
16 changes: 16 additions & 0 deletions tests/ctags/py_constructor_arglist.py.tags
@@ -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

0 comments on commit 3443e28

Please sign in to comment.