Package pyctags :: Module harvesters
[hide private]
[frames] | no frames]

Source Code for Module pyctags.harvesters

  1  ## Copyright (C) 2008 Ben Smith <benjamin.coder.smith@gmail.com> 
  2   
  3  ##    This file is part of pyctags. 
  4   
  5  ##    pyctags is free software: you can redistribute it and/or modify 
  6  ##    it under the terms of the GNU Lesser General Public License as published 
  7  ##    by the Free Software Foundation, either version 3 of the License, or 
  8  ##    (at your option) any later version. 
  9   
 10  ##    pyctags is distributed in the hope that it will be useful, 
 11  ##    but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  ##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 13  ##    GNU General Public License for more details. 
 14   
 15  ##    You should have received a copy of the GNU Lesser General Public License 
 16  ##    and the GNU Lesser General Public Licens along with pyctags.  If not,  
 17  ##    see <http://www.gnu.org/licenses/>. 
 18   
 19  """ Classes that process tag data to collect information.""" 
 20  from copy import copy 
 21   
 22  try: 
 23      # do relative imports for tests 
 24      # try this first in case pyctags is already installed, since we want to be testing the source bundled in the distribution 
 25      from kwargs_validator import the_validator as validator 
 26  except ImportError: 
 27      from pyctags.kwargs_validator import the_validator as validator 
 28   
29 -class base_harvester:
30 """ This class definition outlines the basic interface for harvesting classes."""
31 - def do_before(self):
32 """ Called before any entries are processed with self.feed().""" 33 pass
34
35 - def feed(self, entry):
36 """ Called once per ctags_entry. 37 @param entry: a tag entry to process. 38 @type entry: ctags_entry 39 """ 40 pass
41
42 - def do_after(self):
43 """ Called after all ctags_entry instances are processed with self.feed().""" 44 pass
45
46 - def get_data(self):
47 """ Used to retrieve derived-class specific harvested data.""" 48 pass
49
50 - def process_tag_list(self, taglist):
51 """ 52 Allows processing of a list of ctags_entry instances without an associated ctags_file. 53 @param taglist: list of ctags_entry instances 54 @type taglist: list 55 """ 56 self.do_before() 57 for tag in taglist: 58 self.feed(tag) 59 self.do_after()
60
61 -class kind_harvester(base_harvester):
62 """ Harvests exuberant ctags' extended "kind" information, such as class, member, variable, etc."""
63 - def __init__(self):
64 self.kinds = {}
65
66 - def feed(self, entry):
67 """ 68 Organizes data into a dict with kind as the keys, values are a list of entries of that kind. 69 @param entry: entry to process 70 @type entry: ctags_entry 71 """ 72 if 'kind' in entry.extensions: 73 # note: case sensitive output from exuberant ctags 74 entkey = entry.extensions['kind'] 75 if entkey not in self.kinds: 76 self.kinds[entkey] = list() 77 self.kinds[entkey].append(entry)
78
79 - def get_data(self):
80 """ 81 Gets the dict built with self.feed(). 82 Dict keys are tag kinds, values are lists of ctags_entry instances sporting that kind. 83 @returns: tag data organized by exuberant ctags kind 84 @rtype: dict 85 """ 86 return self.kinds
87
88 -class by_name_harvester(base_harvester):
89 """ Organizes tags by name."""
90 - def __init__(self):
91 self.names = dict()
92
93 - def feed(self, entry):
94 """ 95 Builds a ctags_entry.name keyed dict. 96 """ 97 if entry.name not in self.names: 98 self.names[entry.name] = list() 99 self.names[entry.name].append(entry)
100
101 - def get_data(self):
102 """ 103 Gets the name-organized data. 104 @returns: entries organized with entry.name as key, value is a list of ctags_entry instances that correspond to entry.name 105 @rtype: dict 106 """ 107 return self.names
108
109 -class name_lookup_harvester(base_harvester):
110 """ Builds a sorted list of unique tag names."""
111 - def __init__(self):
112 self.__unique_names = dict() 113 self.__sorted_names = list() 114 self.__name_index = dict()
115
116 - def __len__(self):
117 """ Number of unique tag names found.""" 118 return len(self.__sorted_names)
119
120 - def feed(self, entry):
121 """ Records unique names. 122 @param entry: the entry to collect the name from. 123 @type entry: ctags_entry 124 """ 125 # use dict characteristic of unique keys instead of testing if the key is already there 126 self.__unique_names[entry.name] = None
127
128 - def do_after(self):
129 """ Process the unique names into a form easier to query.""" 130 self.__sorted_names = list(self.__unique_names.keys()) 131 self.__sorted_names.sort() 132 133 i = 0 134 prev_char = self.__sorted_names[0][0] 135 self.__name_index[prev_char] = {'first' : 0} 136 for f in self.__sorted_names: 137 if f[0] not in self.__name_index: 138 self.__name_index[prev_char]['last'] = i - 1 139 self.__name_index[f[0]] = {'first' : i} 140 prev_char = f[0] 141 i += 1 142 self.__name_index[prev_char]['last'] = i
143
144 - def starts_with(self, matchstr, **kwargs):
145 """ 146 Fetches an alphabetical list of unique tag names that begin with matchstr. 147 - B{Parameters:} 148 - B{matchstr:} (str) string to search for in tags db 149 - B{Keyword Arguments:} 150 - B{num_results:} (int) maximum number of results to return, 0 for all, default 151 - B{case_sensitive:} (bool) whether to match case, default False 152 @returns: matching tag names 153 @rtype: list 154 """ 155 156 valid_kwargs = ['num_results', 'case_sensitive'] 157 validator.validate(kwargs.keys(), valid_kwargs) 158 159 final_list = [] 160 case_sensitive = False 161 num_results = 0 162 163 if 'num_results' in kwargs: 164 num_results = int(kwargs['num_results']) 165 166 if len(matchstr) == 0: 167 if num_results: 168 return self.__sorted_names[0:num_results] 169 return self.__sorted_names[:] 170 171 if 'case_sensitive' in kwargs: 172 if kwargs['case_sensitive']: 173 case_sensitive = True 174 175 tag_names_that_start_with_char = [] 176 177 if case_sensitive: 178 if matchstr[0] not in self.__name_index: 179 return [] 180 else: 181 if matchstr[0].lower() not in self.__name_index and matchstr[0].upper() not in self.__name_index: 182 return [] 183 184 if case_sensitive: 185 idxs = self.__name_index[matchstr[0]] 186 187 if idxs['first'] == idxs['last'] + 1: 188 tag_names_that_start_with_char = self.__sorted_names[idxs['first']] 189 else: 190 tag_names_that_start_with_char = self.__sorted_names[idxs['first']:idxs['last'] + 1] 191 192 else: 193 if matchstr[0].lower() in self.__name_index: 194 idxs = self.__name_index[matchstr[0].lower()] 195 196 if idxs['first'] == idxs['last'] + 1: 197 tag_names_that_start_with_char = self.__sorted_names[idxs['first']] 198 else: 199 tag_names_that_start_with_char = self.__sorted_names[idxs['first']:idxs['last'] + 1] 200 201 if matchstr[0].upper() in self.__name_index: 202 idxs = self.__name_index[matchstr[0].upper()] 203 204 if idxs['first'] == idxs['last'] + 1: 205 tag_names_that_start_with_char += [self.__sorted_names[idxs['first']]] 206 else: 207 tag_names_that_start_with_char += self.__sorted_names[idxs['first']:idxs['last'] + 1] 208 209 if len(matchstr) == 1: 210 if num_results == 0: 211 return tag_names_that_start_with_char[:] 212 else: 213 return tag_names_that_start_with_char[0:num_results] 214 215 if case_sensitive: 216 for t in tag_names_that_start_with_char: 217 if (t.find(matchstr) == 0): 218 final_list.append(copy(t)) 219 if num_results > 0 and len(final_list) == num_results: 220 return final_list 221 else: 222 for t in tag_names_that_start_with_char: 223 if (t.lower().find(matchstr.lower()) == 0): 224 final_list.append(copy(t)) 225 if num_results > 0 and len(final_list) == num_results: 226 return final_list 227 228 return final_list
229