hgkw/keyword.py
branchkwmap-templates
changeset 186 c1b7b1d052de
parent 185 bc5cd6cf69bc
child 187 a01a0392f648
equal deleted inserted replaced
185:bc5cd6cf69bc 186:c1b7b1d052de
   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)
   231         if self.renamed(node):
   249         if self.renamed(node):
   232             t2 = super(kwfilelog, self).read(node)
   250             t2 = super(kwfilelog, self).read(node)
   233             return t2 != text
   251             return t2 != text
   234         return super(kwfilelog, self).cmp(node, text)
   252         return super(kwfilelog, self).cmp(node, text)
   235 
   253 
   236 def overwrite(ui, repo, files=None, expand=False):
   254 def overwrite(ui, repo, files=None, expand=True):
   237     '''Expands/shrinks keywords in working directory.'''
   255     '''Expands/shrinks keywords in working directory.'''
   238     wlock = repo.wlock()
   256     wlock = repo.wlock()
   239     try:
   257     try:
   240         ctx = repo.changectx()
   258         ctx = repo.changectx()
   241         if not ctx:
   259         if not ctx:
   255         files = [f for f in files if kwfmatcher(f) and not os.path.islink(f)]
   273         files = [f for f in files if kwfmatcher(f) and not os.path.islink(f)]
   256         if not files:
   274         if not files:
   257             ui.warn(_('given files not tracked or '
   275             ui.warn(_('given files not tracked or '
   258                       'not configured for expansion\n'))
   276                       'not configured for expansion\n'))
   259             return
   277             return
   260         kwt = kwtemplater(ui, repo, node=ctx.node())
   278         kwt = kwtemplater(ui, repo, node=ctx.node(), expand=expand)
   261         kwt.overwrite(files, m, expand=expand, commit=False)
   279         kwt.overwrite(files, m, commit=False)
   262     finally:
   280     finally:
   263         wlock.release()
   281         wlock.release()
   264 
   282 
   265 
   283 
   266 def shrink(ui, repo, *args):
   284 def shrink(ui, repo, *args):
   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,