hgkw/keyword.py
changeset 208 5afdcec8a01f
parent 207 affc18a621f5
child 209 430837dbe7f4
equal deleted inserted replaced
207:affc18a621f5 208:5afdcec8a01f
     1 # keyword.py - keyword expansion for Mercurial
     1 # keyword.py - $Keyword$ expansion for Mercurial
     2 #
     2 #
     3 # Copyright 2007 Christian Ebert <blacktrash@gmx.net>
     3 # Copyright 2007 Christian Ebert <blacktrash@gmx.net>
     4 #
     4 #
     5 # This software may be used and distributed according to the terms
     5 # This software may be used and distributed according to the terms
     6 # of the GNU General Public License, incorporated herein by reference.
     6 # of the GNU General Public License, incorporated herein by reference.
    16 #
    16 #
    17 # For in-depth discussion refer to
    17 # For in-depth discussion refer to
    18 # <http://www.selenic.com/mercurial/wiki/index.cgi/KeywordPlan>.
    18 # <http://www.selenic.com/mercurial/wiki/index.cgi/KeywordPlan>.
    19 #
    19 #
    20 # Keyword expansion is based on Mercurial's changeset template mappings.
    20 # Keyword expansion is based on Mercurial's changeset template mappings.
    21 # The extension provides an additional UTC-date filter ({date|utcdate}).
       
    22 #
       
    23 # Expansions spanning more than one line are truncated to their first line.
       
    24 # Incremental expansion (like CVS' $Log$) is not supported.
       
    25 #
    21 #
    26 # Binary files are not touched.
    22 # Binary files are not touched.
    27 #
    23 #
    28 # Setup in hgrc:
    24 # Setup in hgrc:
    29 #
    25 #
    30 #     # enable extension
    26 #   [extensions]
    31 #     keyword = /full/path/to/keyword.py
    27 #   # enable extension
    32 #     # or, if script in hgext folder:
    28 #   keyword = /full/path/to/hgkw/keyword.py
    33 #     # hgext.keyword =
    29 #   # or, if script in canonical hgext folder:
       
    30 #   # hgext.keyword =
       
    31 #
       
    32 # Files to act upon/ignore are specified in the [keyword] section.
       
    33 # Customized keyword template mappings in the [keywordmaps] section.
       
    34 #
       
    35 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
    34 
    36 
    35 '''keyword expansion in local repositories
    37 '''keyword expansion in local repositories
    36 
    38 
    37 This extension expands RCS/CVS-like or self-customized $Keywords$
    39 This extension expands RCS/CVS-like or self-customized $Keywords$
    38 in the text files selected by your configuration.
    40 in tracked text files selected by your configuration.
    39 
    41 
    40 Keywords are only expanded in local repositories and not logged by
    42 Keywords are only expanded in local repositories and not stored in
    41 Mercurial internally. The mechanism can be regarded as a convenience
    43 the change history. The mechanism can be regarded as a convenience
    42 for the current user or archive distribution.
    44 for the current user or for archive distribution.
    43 
    45 
    44 Configuration is done in the [keyword] and [keywordmaps] sections of
    46 Configuration is done in the [keyword] and [keywordmaps] sections
    45 hgrc files.
    47 of hgrc files.
    46 
    48 
    47 Example:
    49 Example:
    48     [extensions]
       
    49     hgext.keyword =
       
    50 
    50 
    51     [keyword]
    51     [keyword]
    52     # expand keywords in every python file except those matching "x*"
    52     # expand keywords in every python file except those matching "x*"
    53     **.py =
    53     **.py =
    54     x* = ignore
    54     x*    = ignore
    55 
    55 
    56 Note: the more specific you are in your [keyword] filename patterns
    56 Note: the more specific you are in your filename patterns
    57       the less you lose speed in huge repos.
    57       the less you lose speed in huge repos.
    58 
    58 
    59 For a [keywordmaps] template mapping and expansion demonstration
    59 For [keywordmaps] template mapping and expansion demonstration and
    60 run "hg kwdemo".
    60 control run "hg kwdemo".
    61 
    61 
    62 An additional date template filter {date|utcdate} is provided.
    62 An additional date template filter {date|utcdate} is provided.
    63 
    63 
    64 You can replace the default template mappings with customized keywords
    64 The default template mappings (view with "hg kwdemo -d") can be replaced
    65 and templates of your choice.
    65 with customized keywords and templates.
    66 Again, run "hg kwdemo" to control the results of your config changes.
    66 Again, run "hg kwdemo" to control the results of your config changes.
    67 
    67 
    68 When you change keyword configuration, especially the active keywords,
    68 Before changing/disabling active keywords, run "hg kwshrink" to avoid
    69 and do not want to store expanded keywords in change history, run
    69 the risk of inadvertedly storing expanded keywords in the change history.
    70 "hg kwshrink", and then change configuration.
    70 
    71 
    71 Expansions spanning more than one line and incremental expansions,
    72 Expansions spanning more than one line and incremental exapansions
    72 like CVS' $Log$, are not supported. A keyword template map
    73 (like CVS' $Log$) are not supported. A keyword template map
       
    74 "Log = {desc}" expands to the first line of the changeset description.
    73 "Log = {desc}" expands to the first line of the changeset description.
    75 
    74 
    76 Caveat: "hg import" fails if the patch context contains an active
    75 Caveat: "hg import" fails if the patch context contains an active
    77         keyword. In that case run "hg kwshrink", reimport, and then
    76         keyword. In that case run "hg kwshrink", reimport, and then
    78         "hg kwexpand".
    77         "hg kwexpand".
   137             except TypeError:
   136             except TypeError:
   138                 # depending on hg rev changeset_templater has extra "brinfo" arg
   137                 # depending on hg rev changeset_templater has extra "brinfo" arg
   139                 self.t = cmdutil.changeset_templater(self.ui, self.repo,
   138                 self.t = cmdutil.changeset_templater(self.ui, self.repo,
   140                                                      False, None, '', False)
   139                                                      False, None, '', False)
   141 
   140 
   142     def ctxnode(self, node):
   141     def _ctxnode(self, node):
   143         '''Obtains missing node from file context.'''
   142         '''Obtains missing node from file context.'''
   144         if not self.node:
   143         if not self.node:
   145             c = context.filectx(self.repo, self.path, fileid=node)
   144             c = context.filectx(self.repo, self.path, fileid=node)
   146             self.node = c.node()
   145             self.node = c.node()
   147 
   146 
   148     def kwsub(self, mobj):
   147     def _kwsub(self, mobj):
   149         '''Substitutes keyword using corresponding template.'''
   148         '''Substitutes keyword using corresponding template.'''
   150         kw = mobj.group(1)
   149         kw = mobj.group(1)
   151         self.t.use_template(self.templates[kw])
   150         self.t.use_template(self.templates[kw])
   152         self.ui.pushbuffer()
   151         self.ui.pushbuffer()
   153         self.t.show(changenode=self.node, root=self.repo.root, file=self.path)
   152         self.t.show(changenode=self.node, root=self.repo.root, file=self.path)
   156 
   155 
   157     def expand(self, node, data):
   156     def expand(self, node, data):
   158         '''Returns data with keywords expanded.'''
   157         '''Returns data with keywords expanded.'''
   159         if util.binary(data):
   158         if util.binary(data):
   160             return data
   159             return data
   161         self.ctxnode(node)
   160         self._ctxnode(node)
   162         return self.re_kw.sub(self.kwsub, data)
   161         return self.re_kw.sub(self._kwsub, data)
   163 
   162 
   164     def process(self, node, data):
   163     def process(self, node, data):
   165         '''Returns a tuple: data, count.
   164         '''Returns a tuple: data, count.
   166         Count is number of keywords/keyword substitutions.
   165         Count is number of keywords/keyword substitutions.
   167         Keywords in data are expanded, if templater was initialized.'''
   166         Keywords in data are expanded, if templater was initialized.'''
   168         if util.binary(data):
   167         if util.binary(data):
   169             return data, None
   168             return data, None
   170         if self.t:
   169         if self.t:
   171             self.ctxnode(node)
   170             self._ctxnode(node)
   172             return self.re_kw.subn(self.kwsub, data)
   171             return self.re_kw.subn(self._kwsub, data)
   173         return data, self.re_kw.search(data)
   172         return data, self.re_kw.search(data)
   174 
   173 
   175     def shrink(self, text):
   174     def shrink(self, text):
   176         '''Returns text with all keyword substitutions removed.'''
   175         '''Returns text with all keyword substitutions removed.'''
   177         if util.binary(text):
   176         if util.binary(text):
   278 
   277 
   279 
   278 
   280 def shrink(ui, repo, *args):
   279 def shrink(ui, repo, *args):
   281     '''revert expanded keywords in working directory
   280     '''revert expanded keywords in working directory
   282 
   281 
   283     run before:
   282     run before changing/disabling active keywords
   284                disabling keyword expansion
   283     or if you experience problems with "hg import" or "hg merge"
   285                changing keyword expansion configuration
       
   286     or if you experience problems with "hg import"
       
   287     '''
   284     '''
   288     expand = False
   285     expand = False
   289     _overwrite(ui, repo, args, expand)
   286     _overwrite(ui, repo, args, expand)
   290 
   287 
   291 def expand(ui, repo, *args):
   288 def expand(ui, repo, *args):
   319             ui.setconfig('keywordmaps', k.strip(), v.strip())
   316             ui.setconfig('keywordmaps', k.strip(), v.strip())
   320         if opts['rcfile']:
   317         if opts['rcfile']:
   321             ui.readconfig(opts['rcfile'])
   318             ui.readconfig(opts['rcfile'])
   322         kwmaps = (dict(ui.configitems('keywordmaps')) or
   319         kwmaps = (dict(ui.configitems('keywordmaps')) or
   323                   kwtemplater.deftemplates)
   320                   kwtemplater.deftemplates)
       
   321     for k, v in ui.configitems('extensions'):
       
   322         if k.endswith('keyword'):
       
   323             extension = '%s = %s' % (k, v)
       
   324             break
   324     tmpdir = tempfile.mkdtemp('', 'kwdemo.')
   325     tmpdir = tempfile.mkdtemp('', 'kwdemo.')
   325     ui.note(_('creating temporary repo at %s\n') % tmpdir)
   326     ui.note(_('creating temporary repo at %s\n') % tmpdir)
   326     repo = localrepo.localrepository(ui, path=tmpdir, create=True)
   327     repo = localrepo.localrepository(ui, path=tmpdir, create=True)
   327     repo.ui = ui # backwards compatibility
   328     repo.ui = ui # backwards compatibility
   328     reposetup(ui, repo)
   329     reposetup(ui, repo)
   329     ui.status(_('config with %s keyword template maps:\n') % kwstatus)
   330     ui.status(_('config using %s keyword template maps:\n') % kwstatus)
   330     ui.write('[keyword]\n%s =\n[keywordmaps]\n' % fn)
   331     ui.write('[extensions]\n%s\n'
       
   332              '[keyword]\n%s =\n'
       
   333              '[keywordmaps]\n' % (extension, fn))
   331     for k, v in kwmaps.items():
   334     for k, v in kwmaps.items():
   332         ui.write('%s = %s\n' % (k, v))
   335         ui.write('%s = %s\n' % (k, v))
   333     path = repo.wjoin(fn)
   336     path = repo.wjoin(fn)
   334     keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
   337     keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
   335     repo.wopener(fn, 'w').write(keywords)
   338     repo.wopener(fn, 'w').write(keywords)