hgkw/keyword.py
branchkwmap-templates
changeset 181 8019554adbb2
parent 180 5d20f5d642f3
child 182 de32fbee75a4
equal deleted inserted replaced
180:5d20f5d642f3 181:8019554adbb2
    92 
    92 
    93 def utcdate(date):
    93 def utcdate(date):
    94     '''Returns hgdate in cvs-like UTC format.'''
    94     '''Returns hgdate in cvs-like UTC format.'''
    95     return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
    95     return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
    96 
    96 
    97 def kwdemo(ui, repo, **opts):
    97 def getcmd(ui):
       
    98     '''Returns current hg command.'''
       
    99     # commands.parse(ui, sys.argv[1:])[0] breaks "hg diff -r"
       
   100     try:
       
   101         args = fancyopts.fancyopts(sys.argv[1:], commands.globalopts, {})
       
   102     except fancyopts.getopt.GetoptError, inst:
       
   103         raise commands.ParseError(None, inst)
       
   104     if args:
       
   105         cmd = args[0]
       
   106         aliases, i = findcmd(ui, cmd)
       
   107         return aliases[0]
       
   108 
       
   109 class kwtemplater(object):
       
   110     '''
       
   111     Sets up keyword templates, corresponding keyword regex, and
       
   112     provides keyword substitution functions.
       
   113     '''
       
   114     def __init__(self, ui, repo, path='', node=None):
       
   115         self.ui = ui
       
   116         self.repo = repo
       
   117         self.path = path
       
   118         self.node = node
       
   119         templates = dict(ui.configitems('keywordmaps'))
       
   120         if templates:
       
   121             # parse templates here for less overhead in kwsub matchfunc
       
   122             for k in templates.keys():
       
   123                 templates[k] = templater.parsestring(templates[k],
       
   124                                                      quoted=False)
       
   125         self.templates = templates or deftemplates
       
   126         escaped = [re.escape(k) for k in self.templates.keys()]
       
   127         self.re_kw = re.compile(r'\$(%s)[^$]*?\$' % '|'.join(escaped))
       
   128         templater.common_filters['utcdate'] = utcdate
       
   129         try:
       
   130             self.t = cmdutil.changeset_templater(ui, repo, False, '', False)
       
   131         except TypeError:
       
   132             # depending on hg rev changeset_templater has extra "brinfo" arg
       
   133             self.t = cmdutil.changeset_templater(ui, repo,
       
   134                                                  False, None, '', False)
       
   135 
       
   136     def kwsub(self, mobj):
       
   137         '''Substitutes keyword using corresponding template.'''
       
   138         kw = mobj.group(1)
       
   139         self.t.use_template(self.templates[kw])
       
   140         self.ui.pushbuffer()
       
   141         self.t.show(changenode=self.node, root=self.repo.root, file=self.path)
       
   142         keywordsub = templater.firstline(self.ui.popbuffer())
       
   143         return '$%s: %s $' % (kw, keywordsub)
       
   144 
       
   145     def expand(self, node, data):
       
   146         '''Returns data with expanded keywords.'''
       
   147         if util.binary(data):
       
   148             return data
       
   149         c = context.filectx(self.repo, self.path, fileid=node)
       
   150         self.node = c.node()
       
   151         return self.re_kw.sub(self.kwsub, data)
       
   152 
       
   153     def shrink(self, text):
       
   154         '''Returns text with all keyword substitutions removed.'''
       
   155         if util.binary(text):
       
   156             return text
       
   157         return self.re_kw.sub(r'$\1$', text)
       
   158 
       
   159     def overwrite(self, candidates, mn):
       
   160         '''Overwrites candidates in working dir expanding keywords.'''
       
   161         files = []
       
   162         m = self.repo.manifest.read(mn)
       
   163         for f in candidates:
       
   164             data = self.repo.wread(f)
       
   165             if not util.binary(data):
       
   166                 self.path = f
       
   167                 data, kwct = self.re_kw.subn(self.kwsub, data)
       
   168                 if kwct:
       
   169                     self.ui.debug(_('overwriting %s expanding keywords\n') % f)
       
   170                     self.repo.wwrite(f, data, m.flags(f))
       
   171                     files.append(f)
       
   172         if files:
       
   173             self.repo.dirstate.update(files, 'n')
       
   174 
       
   175 class kwfilelog(filelog.filelog):
       
   176     '''
       
   177     Subclass of filelog to hook into its read, add, cmp methods.
       
   178     Keywords are "stored" unexpanded, and expanded on reading.
       
   179     '''
       
   180     def __init__(self, opener, path, kwtemplater):
       
   181         super(kwfilelog, self).__init__(opener, path)
       
   182         self.kwtemplater = kwtemplater
       
   183 
       
   184     def read(self, node):
       
   185         '''Substitutes keywords when reading filelog.'''
       
   186         data = super(kwfilelog, self).read(node)
       
   187         return self.kwtemplater.expand(node, data)
       
   188 
       
   189     def add(self, text, meta, tr, link, p1=None, p2=None):
       
   190         '''Removes keyword substitutions when adding to filelog.'''
       
   191         text = self.kwtemplater.shrink(text)
       
   192         return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2)
       
   193 
       
   194     def cmp(self, node, text):
       
   195         '''Removes keyword substitutions for comparison.'''
       
   196         text = self.kwtemplater.shrink(text)
       
   197         if self.renamed(node):
       
   198             t2 = super(kwfilelog, self).read(node)
       
   199             return t2 != text
       
   200         return super(kwfilelog, self).cmp(node, text)
       
   201 
       
   202 
       
   203 def demo(ui, repo, **opts):
    98     '''print [keywordmaps] configuration and an expansion example
   204     '''print [keywordmaps] configuration and an expansion example
    99     '''
   205     '''
   100     log = 'hg keyword config and expansion example'
   206     log = 'hg keyword config and expansion example'
   101     fn = 'demo.txt'
   207     fn = 'demo.txt'
   102     tmpdir = tempfile.mkdtemp('', 'kwdemo.')
   208     tmpdir = tempfile.mkdtemp('', 'kwdemo.')
   133         ui.status(_('\n%s keywords expanded:\n') % kwstatus)
   239         ui.status(_('\n%s keywords expanded:\n') % kwstatus)
   134     ui.write(_repo.wread(fn))
   240     ui.write(_repo.wread(fn))
   135     ui.debug(_('\nremoving temporary repo\n'))
   241     ui.debug(_('\nremoving temporary repo\n'))
   136     shutil.rmtree(tmpdir)
   242     shutil.rmtree(tmpdir)
   137 
   243 
   138 def getcmd(ui):
       
   139     '''Returns current hg command.'''
       
   140     # commands.parse(ui, sys.argv[1:])[0] breaks "hg diff -r"
       
   141     try:
       
   142         args = fancyopts.fancyopts(sys.argv[1:], commands.globalopts, {})
       
   143     except fancyopts.getopt.GetoptError, inst:
       
   144         raise commands.ParseError(None, inst)
       
   145     if args:
       
   146         cmd = args[0]
       
   147         aliases, i = findcmd(ui, cmd)
       
   148         return aliases[0]
       
   149 
       
   150 class kwtemplater(object):
       
   151     '''
       
   152     Sets up keyword templates, corresponding keyword regex, and
       
   153     provides keyword substitution functions.
       
   154     '''
       
   155     def __init__(self, ui, repo, path='', node=None):
       
   156         self.ui = ui
       
   157         self.repo = repo
       
   158         self.path = path
       
   159         self.node = node
       
   160         templates = dict(ui.configitems('keywordmaps'))
       
   161         if templates:
       
   162             # parse templates here for less overhead in kwsub matchfunc
       
   163             for k in templates.keys():
       
   164                 templates[k] = templater.parsestring(templates[k],
       
   165                                                      quoted=False)
       
   166         self.templates = templates or deftemplates
       
   167         escaped = [re.escape(k) for k in self.templates.keys()]
       
   168         self.re_kw = re.compile(r'\$(%s)[^$]*?\$' % '|'.join(escaped))
       
   169         templater.common_filters['utcdate'] = utcdate
       
   170         try:
       
   171             self.t = cmdutil.changeset_templater(ui, repo, False, '', False)
       
   172         except TypeError:
       
   173             # depending on hg rev changeset_templater has extra "brinfo" arg
       
   174             self.t = cmdutil.changeset_templater(ui, repo,
       
   175                                                  False, None, '', False)
       
   176 
       
   177     def kwsub(self, mobj):
       
   178         '''Substitutes keyword using corresponding template.'''
       
   179         kw = mobj.group(1)
       
   180         self.t.use_template(self.templates[kw])
       
   181         self.ui.pushbuffer()
       
   182         self.t.show(changenode=self.node, root=self.repo.root, file=self.path)
       
   183         keywordsub = templater.firstline(self.ui.popbuffer())
       
   184         return '$%s: %s $' % (kw, keywordsub)
       
   185 
       
   186     def expand(self, node, data):
       
   187         '''Returns data with expanded keywords.'''
       
   188         if util.binary(data):
       
   189             return data
       
   190         c = context.filectx(self.repo, self.path, fileid=node)
       
   191         self.node = c.node()
       
   192         return self.re_kw.sub(self.kwsub, data)
       
   193 
       
   194     def shrink(self, text):
       
   195         '''Returns text with all keyword substitutions removed.'''
       
   196         if util.binary(text):
       
   197             return text
       
   198         return self.re_kw.sub(r'$\1$', text)
       
   199 
       
   200     def overwrite(self, candidates, mn):
       
   201         '''Overwrites candidates in working dir expanding keywords.'''
       
   202         files = []
       
   203         m = self.repo.manifest.read(mn)
       
   204         for f in candidates:
       
   205             data = self.repo.wread(f)
       
   206             if not util.binary(data):
       
   207                 self.path = f
       
   208                 data, kwct = self.re_kw.subn(self.kwsub, data)
       
   209                 if kwct:
       
   210                     self.ui.debug(_('overwriting %s expanding keywords\n') % f)
       
   211                     self.repo.wwrite(f, data, m.flags(f))
       
   212                     files.append(f)
       
   213         if files:
       
   214             self.repo.dirstate.update(files, 'n')
       
   215 
       
   216 class kwfilelog(filelog.filelog):
       
   217     '''
       
   218     Subclass of filelog to hook into its read, add, cmp methods.
       
   219     Keywords are "stored" unexpanded, and expanded on reading.
       
   220     '''
       
   221     def __init__(self, opener, path, kwtemplater):
       
   222         super(kwfilelog, self).__init__(opener, path)
       
   223         self.kwtemplater = kwtemplater
       
   224 
       
   225     def read(self, node):
       
   226         '''Substitutes keywords when reading filelog.'''
       
   227         data = super(kwfilelog, self).read(node)
       
   228         return self.kwtemplater.expand(node, data)
       
   229 
       
   230     def add(self, text, meta, tr, link, p1=None, p2=None):
       
   231         '''Removes keyword substitutions when adding to filelog.'''
       
   232         text = self.kwtemplater.shrink(text)
       
   233         return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2)
       
   234 
       
   235     def cmp(self, node, text):
       
   236         '''Removes keyword substitutions for comparison.'''
       
   237         text = self.kwtemplater.shrink(text)
       
   238         if self.renamed(node):
       
   239             t2 = super(kwfilelog, self).read(node)
       
   240             return t2 != text
       
   241         return super(kwfilelog, self).cmp(node, text)
       
   242 
       
   243 
       
   244 def reposetup(ui, repo):
   244 def reposetup(ui, repo):
   245     '''Sets up repo as kwrepo for keyword substitution.
   245     '''Sets up repo as kwrepo for keyword substitution.
   246     Overrides file method to return kwfilelog instead of filelog
   246     Overrides file method to return kwfilelog instead of filelog
   247     if file matches user configuration.
   247     if file matches user configuration.
   248     Wraps commit to overwrite configured files with updated
   248     Wraps commit to overwrite configured files with updated
   264 
   264 
   265     kwfmatcher = util.matcher(repo.root, inc=inc, exc=exc)[1]
   265     kwfmatcher = util.matcher(repo.root, inc=inc, exc=exc)[1]
   266     ui = repo.ui
   266     ui = repo.ui
   267 
   267 
   268     class kwrepo(repo.__class__):
   268     class kwrepo(repo.__class__):
   269         '''
       
   270         Subclass of repo's class attribute to wrap its file and commit methods.
       
   271         '''
       
   272 
       
   273         def file(self, f):
   269         def file(self, f):
   274             '''Opens kwfilelog instead of filelog if needed.'''
       
   275             if f[0] == '/':
   270             if f[0] == '/':
   276                 f = f[1:]
   271                 f = f[1:]
   277             if kwfmatcher(f):
   272             if kwfmatcher(f):
   278                 kwt = kwtemplater(ui, self, path=f)
   273                 kwt = kwtemplater(ui, self, path=f)
   279                 return kwfilelog(self.sopener, f, kwt)
   274                 return kwfilelog(self.sopener, f, kwt)
   281                 return filelog.filelog(self.sopener, f)
   276                 return filelog.filelog(self.sopener, f)
   282 
   277 
   283         def commit(self, files=None, text='', user=None, date=None,
   278         def commit(self, files=None, text='', user=None, date=None,
   284                    match=util.always, force=False, lock=None, wlock=None,
   279                    match=util.always, force=False, lock=None, wlock=None,
   285                    force_editor=False, p1=None, p2=None, extra={}):
   280                    force_editor=False, p1=None, p2=None, extra={}):
   286             '''Wraps commit, expanding keywords of committed and
       
   287             configured files in working directory.'''
       
   288             wrelease = False
   281             wrelease = False
   289             if not wlock:
   282             if not wlock:
   290                 wlock = self.wlock()
   283                 wlock = self.wlock()
   291                 wrelease = True
   284                 wrelease = True
   292             try:
   285             try:
   318     repo.__class__ = kwrepo
   311     repo.__class__ = kwrepo
   319 
   312 
   320 
   313 
   321 cmdtable = {
   314 cmdtable = {
   322     'kwdemo':
   315     'kwdemo':
   323         (kwdemo,
   316         (demo,
   324          [('d', 'default', None, _('use default keyword maps'))],
   317          [('d', 'default', None, _('use default keyword maps'))],
   325          _('hg kwdemo [-d]')),
   318          _('hg kwdemo [-d]')),
   326 }
   319 }