134 class kwtemplater(object): |
134 class kwtemplater(object): |
135 ''' |
135 ''' |
136 Sets up keyword templates, corresponding keyword regex, and |
136 Sets up keyword templates, corresponding keyword regex, and |
137 provides keyword substitution functions. |
137 provides keyword substitution functions. |
138 ''' |
138 ''' |
139 def __init__(self, ui, repo, path='', node=None): |
139 def __init__(self, ui, repo, path='', node=None, expand=True): |
140 self.ui = ui |
140 self.ui = ui |
141 self.repo = repo |
141 self.repo = repo |
142 self.path = path |
142 self.path = path |
143 self.node = node |
143 self.node = node |
144 templates = dict(ui.configitems('keywordmaps')) |
144 templates = dict(ui.configitems('keywordmaps')) |
145 if templates: |
145 if templates: |
146 # parse templates here for less overhead in kwsub matchfunc |
|
147 for k in templates.keys(): |
146 for k in templates.keys(): |
148 templates[k] = templater.parsestring(templates[k], |
147 templates[k] = templater.parsestring(templates[k], |
149 quoted=False) |
148 quoted=False) |
150 self.templates = templates or deftemplates |
149 self.templates = templates or deftemplates |
151 escaped = [re.escape(k) for k in self.templates.keys()] |
150 escaped = [re.escape(k) for k in self.templates.keys()] |
152 self.re_kw = re.compile(r'\$(%s)[^$]*?\$' % '|'.join(escaped)) |
151 self.re_kw = re.compile(r'\$(%s)[^$]*?\$' % '|'.join(escaped)) |
153 templater.common_filters['utcdate'] = utcdate |
152 if expand: |
154 try: |
153 templater.common_filters['utcdate'] = utcdate |
155 self.t = cmdutil.changeset_templater(ui, repo, False, '', False) |
154 try: |
156 except TypeError: |
155 self.t = cmdutil.changeset_templater(ui, repo, |
157 # depending on hg rev changeset_templater has extra "brinfo" arg |
156 False, '', False) |
158 self.t = cmdutil.changeset_templater(ui, repo, |
157 except TypeError: |
159 False, None, '', False) |
158 # depending on hg rev changeset_templater has extra "brinfo" arg |
|
159 self.t = cmdutil.changeset_templater(ui, repo, |
|
160 False, None, '', False) |
|
161 else: |
|
162 self.t = None |
|
163 |
|
164 def ctxnode(self, node): |
|
165 '''Obtains missing node from file context.''' |
|
166 if not self.node: |
|
167 c = context.filectx(self.repo, self.path, fileid=node) |
|
168 self.node = c.node() |
160 |
169 |
161 def kwsub(self, mobj): |
170 def kwsub(self, mobj): |
162 '''Substitutes keyword using corresponding template.''' |
171 '''Substitutes keyword using corresponding template.''' |
163 kw = mobj.group(1) |
172 kw = mobj.group(1) |
164 self.t.use_template(self.templates[kw]) |
173 self.t.use_template(self.templates[kw]) |
166 self.t.show(changenode=self.node, root=self.repo.root, file=self.path) |
175 self.t.show(changenode=self.node, root=self.repo.root, file=self.path) |
167 keywordsub = templater.firstline(self.ui.popbuffer()) |
176 keywordsub = templater.firstline(self.ui.popbuffer()) |
168 return '$%s: %s $' % (kw, keywordsub) |
177 return '$%s: %s $' % (kw, keywordsub) |
169 |
178 |
170 def expand(self, node, data): |
179 def expand(self, node, data): |
171 '''Returns data with expanded keywords.''' |
180 '''Returns data with keywords expanded.''' |
172 if util.binary(data): |
181 if util.binary(data): |
173 return data |
182 return data |
174 c = context.filectx(self.repo, self.path, fileid=node) |
183 self.ctxnode(node) |
175 self.node = c.node() |
|
176 return self.re_kw.sub(self.kwsub, data) |
184 return self.re_kw.sub(self.kwsub, data) |
|
185 |
|
186 def process(self, node, data): |
|
187 '''Returns a tuple: data, count. |
|
188 Count is number of keywords/keyword substitutions. |
|
189 Keywords in data are expanded, if templater was initialized.''' |
|
190 if util.binary(data): |
|
191 return data, None |
|
192 if self.t: |
|
193 self.ctxnode(node) |
|
194 return self.re_kw.subn(self.kwsub, data) |
|
195 return data, self.re_kw.search(data) |
177 |
196 |
178 def shrink(self, text): |
197 def shrink(self, text): |
179 '''Returns text with all keyword substitutions removed.''' |
198 '''Returns text with all keyword substitutions removed.''' |
180 if util.binary(text): |
199 if util.binary(text): |
181 return text |
200 return text |
182 return self.re_kw.sub(r'$\1$', text) |
201 return self.re_kw.sub(r'$\1$', text) |
183 |
202 |
184 def overwrite(self, candidates, manifest, expand=True, commit=True): |
203 def overwrite(self, candidates, man, commit=True): |
185 '''Overwrites candidates in working dir expanding keywords.''' |
204 '''Overwrites files in working directory if keywords are detected. |
186 if expand: |
205 Keywords are expanded if keyword templater is initialized, |
187 sub = self.kwsub |
206 otherwise their substitution is removed.''' |
188 action = 'expanding' |
207 expand = self.t is not None |
189 else: |
208 action = ('shrinking', 'expanding')[expand] |
190 sub = r'$\1$' |
209 notify = (self.ui.note, self.ui.debug)[commit] |
191 action = 'shrinking' |
|
192 if not commit: |
|
193 notify = self.ui.note |
|
194 else: |
|
195 notify = self.ui.debug |
|
196 files = [] |
210 files = [] |
197 for f in candidates: |
211 for f in candidates: |
198 data = self.repo.wread(f) |
212 fp = self.repo.file(f, kwcnt=True, kwexp=expand) |
199 if not util.binary(data): |
213 data, cnt = fp.read(man[f]) |
200 self.path = f |
214 if cnt: |
201 data, kwct = self.re_kw.subn(sub, data) |
215 notify(_('overwriting %s %s keywords\n') % (f, action)) |
202 if kwct: |
216 self.repo.wwrite(f, data, man.flags(f)) |
203 notify(_('overwriting %s %s keywords\n') % (f, action)) |
217 files.append(f) |
204 self.repo.wwrite(f, data, manifest.flags(f)) |
|
205 files.append(f) |
|
206 if files: |
218 if files: |
207 self.repo.dirstate.update(files, 'n') |
219 self.repo.dirstate.update(files, 'n') |
208 |
220 |
209 class kwfilelog(filelog.filelog): |
221 class kwfilelog(filelog.filelog): |
210 ''' |
222 ''' |
211 Subclass of filelog to hook into its read, add, cmp methods. |
223 Subclass of filelog to hook into its read, add, cmp methods. |
212 Keywords are "stored" unexpanded, and expanded on reading. |
224 Keywords are "stored" unexpanded, and processed on reading. |
213 ''' |
225 ''' |
214 def __init__(self, opener, path, kwtemplater): |
226 def __init__(self, opener, path, kwtemplater, kwcnt): |
215 super(kwfilelog, self).__init__(opener, path) |
227 super(kwfilelog, self).__init__(opener, path) |
216 self.kwtemplater = kwtemplater |
228 self.kwtemplater = kwtemplater |
|
229 self.kwcnt = kwcnt |
217 |
230 |
218 def read(self, node): |
231 def read(self, node): |
219 '''Substitutes keywords when reading filelog.''' |
232 '''Passes data through kwemplater methods for |
|
233 either unconditional keyword expansion |
|
234 or counting of keywords and substitution method |
|
235 set by the calling overwrite function.''' |
220 data = super(kwfilelog, self).read(node) |
236 data = super(kwfilelog, self).read(node) |
221 return self.kwtemplater.expand(node, data) |
237 if not self.kwcnt: |
|
238 return self.kwtemplater.expand(node, data) |
|
239 return self.kwtemplater.process(node, data) |
222 |
240 |
223 def add(self, text, meta, tr, link, p1=None, p2=None): |
241 def add(self, text, meta, tr, link, p1=None, p2=None): |
224 '''Removes keyword substitutions when adding to filelog.''' |
242 '''Removes keyword substitutions when adding to filelog.''' |
225 text = self.kwtemplater.shrink(text) |
243 text = self.kwtemplater.shrink(text) |
226 return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2) |
244 return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2) |
269 run before: |
287 run before: |
270 disabling keyword expansion |
288 disabling keyword expansion |
271 changing keyword expansion configuration |
289 changing keyword expansion configuration |
272 or if you experience problems with "hg import" |
290 or if you experience problems with "hg import" |
273 ''' |
291 ''' |
274 overwrite(ui, repo, args, expand=False) |
292 overwrite(ui, repo, files=args, expand=False) |
275 |
293 |
276 def expand(ui, repo, *args): |
294 def expand(ui, repo, *args): |
277 '''expand keywords in working directory |
295 '''expand keywords in working directory |
278 |
296 |
279 run after (re)enabling keyword expansion |
297 run after (re)enabling keyword expansion |
280 ''' |
298 ''' |
281 overwrite(ui, repo, args, expand=True) |
299 overwrite(ui, repo, files=args) |
282 |
300 |
283 def demo(ui, repo, **opts): |
301 def demo(ui, repo, **opts): |
284 '''print [keywordmaps] configuration and an expansion example |
302 '''print [keywordmaps] configuration and an expansion example |
285 |
303 |
286 Show current or default keyword template maps and their expansion |
304 Show current or default keyword template maps and their expansion |
340 kwfmatcher = keywordmatcher(ui, repo) |
358 kwfmatcher = keywordmatcher(ui, repo) |
341 if kwfmatcher is None: |
359 if kwfmatcher is None: |
342 return |
360 return |
343 |
361 |
344 class kwrepo(repo.__class__): |
362 class kwrepo(repo.__class__): |
345 def file(self, f): |
363 def file(self, f, kwcnt=False, kwexp=True): |
346 if f[0] == '/': |
364 if f[0] == '/': |
347 f = f[1:] |
365 f = f[1:] |
348 if kwfmatcher(f): |
366 if kwfmatcher(f): |
349 kwt = kwtemplater(ui, self, path=f) |
367 kwt = kwtemplater(ui, self, path=f, expand=kwexp) |
350 return kwfilelog(self.sopener, f, kwt) |
368 return kwfilelog(self.sopener, f, kwt, kwcnt) |
351 else: |
369 else: |
352 return filelog.filelog(self.sopener, f) |
370 return filelog.filelog(self.sopener, f) |
353 |
371 |
354 def commit(self, files=None, text='', user=None, date=None, |
372 def commit(self, files=None, text='', user=None, date=None, |
355 match=util.always, force=False, lock=None, wlock=None, |
373 match=util.always, force=False, lock=None, wlock=None, |