1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 """
20 Python representation of ctags data elements.
21
22 This module uses the eval function, which will let package users execute arbitrary python code, if they want.
23 """
24
25 from copy import copy
26 import os
27 try:
28
29
30 from kwargs_validator import the_validator as validator
31 except ImportError:
32 from pyctags.kwargs_validator import the_validator as validator
33
34 _PYTHON_3000_ = True
35
36 import sys
37 if sys.version_info[0] < 3:
38 _PYTHON_3000_ = False
39
40 _COMMENT_BEGIN_ = ';"'
41
43 """
44 An entry in the tag file.
45 """
47 """
48 A tag entry from ctags file. Initializes from str or keyword args.
49 - B{Optional Parameters:}
50 - B{args[0]}: (str) a ctags_entry repr or string from a tag file
51 - B{Keyword Arguments:}
52 - B{name}: (str) tag name
53 - B{file}: (str) source file name
54 - B{pattern}: (str) locator pattern for tag
55 - B{line_number}: (int) locator line number
56 - B{extensions}: (dict) extension fields
57 - B{Raises:}
58 - B{ValueError}: line_number or pattern isn't set, or a parameter type can't be transformed.
59 """
60 valid_kwargs = ['name', 'file', 'pattern', 'line_number', 'extensions']
61 validator.validate(kwargs.keys(), valid_kwargs)
62
63 self.name = None
64 """ Tag name."""
65 self.file = None
66 """ Source file of tag."""
67 self.pattern = None
68 """ If not None, regular expression to locate this tag in self.file."""
69 self.line_number = None
70 """ If not None, line number to locate this tag in self.file."""
71 self.extensions = None
72 """ If not none, dict of extension fields embedded in comments in the tag entry, from exuberant ctags."""
73 self.__rep = None
74
75 entry = dict()
76 if len(args) == 1:
77 if len(kwargs):
78 raise ValueError("multiple tag data found in init")
79
80 if type(args[0]) == dict:
81 entry = args[0]
82
83 elif (type(args[0]) == str or type(args[0]) == unicode) and len(args[0]):
84 if args[0][0] == '{' and args[0][-1] == '}':
85
86
87 entry = eval(args[0])
88
89 else:
90 argstr = args[0].strip()
91
92 if not _PYTHON_3000_ and type(argstr) is not unicode:
93 argstr = unicode(argstr, "utf-8")
94
95
96 (entry['name'], entry['file'], the_rest) = argstr.split('\t', 2)
97
98 extension_fields = None
99
100 if the_rest.find(_COMMENT_BEGIN_) > 0:
101 (locator, junk, extension_fields) = the_rest.rpartition(_COMMENT_BEGIN_)
102 else:
103 locator = the_rest
104
105 if locator.isdigit():
106 try:
107 entry['line_number'] = int(locator)
108 except ValueError:
109 raise ValueError("Line number locator found for tag, but can't be converted to integer")
110 else:
111
112 entry['pattern'] = locator
113
114 entry['extensions'] = {}
115 kind_arg_found = False
116 if extension_fields:
117 if extension_fields[0] == '\t':
118
119
120 extension_list = extension_fields[1:].split('\t')
121 for ext in extension_list:
122 if ':' in ext:
123 (k, v) = ext.split(':', 1)
124 entry['extensions'][k] = v
125 if k == 'line' and 'line_number' not in entry:
126 try:
127 entry['line_number'] = int(v)
128 except ValueError:
129 raise ValueError("Extended tag 'line' found but can't be converted to integer.")
130 else:
131 if kind_arg_found:
132 raise ValueError("Unknown extended tag found.")
133 else:
134 entry['extensions']['kind'] = ext
135 kind_arg_found = True
136
137 elif len(kwargs):
138 entry = kwargs
139
140 if 'file' in entry:
141 self.file = entry['file']
142 else:
143 raise ValueError("'file' parameter is required")
144
145 if 'name' in entry:
146 self.name = entry['name']
147 else:
148 raise ValueError("'name' parameter is required")
149
150 if 'pattern' in entry:
151 self.pattern = entry['pattern']
152
153 if 'line_number' in entry:
154 self.line_number = int(entry['line_number'])
155
156 if not self.line_number and not self.pattern:
157 raise ValueError("No valid locator for this tag.")
158
159 if 'extensions' in entry:
160 self.extensions = entry['extensions']
161
162
163 self.__rep = entry
164
166 return str(self.__rep)
167
169 idx = self.file.rfind('/')
170
171 if idx == -1:
172 idx = self.file.rfind("\\")
173
174 short_fn = self.file[idx + 1:]
175
176 if self.name:
177 return self.name + ':' + short_fn + ':' + str(self.line_number)
178 else:
179 return "Unnamed tag."
180
182 return (repr(self) == repr(other))
183
185 return (repr(self) != repr(other))
186