(Move categories to tip template) |
(Add Related plugins section.) |
||
(4 intermediate revisions by 2 users not shown) | |||
Line 3: | Line 3: | ||
|previous=1520 |
|previous=1520 |
||
|next=1522 |
|next=1522 |
||
− | |created= |
+ | |created=2007 |
|complexity=advanced |
|complexity=advanced |
||
|author=Fritzophrenic |
|author=Fritzophrenic |
||
|version=7.0 |
|version=7.0 |
||
+ | |subpage=/200712 |
||
|category1=Advanced Regex |
|category1=Advanced Regex |
||
|category2=Automated Text Insertion |
|category2=Automated Text Insertion |
||
Line 24: | Line 25: | ||
An explanation of the command string follows: |
An explanation of the command string follows: |
||
− | *'''< |
+ | *'''<code>g#</code>''' - search and replace only on a line matching copyright notice patten. '#' is used rather than the customary '/' to avoid confusion with all the '\' characters. |
− | :*'''< |
+ | :*'''<code>\\cCOPYRIGHT </code>''' - match on text COPYRIGHT followed by a space (case-insensitive) |
− | :*'''< |
+ | :*'''<code>\\(".strftime("%Y")."\\)\\@!</code>''' - only match when the entire match does not consist of the current year (i.e. don't update a notice containing only the current year, like "Copyright 2007"). |
− | :*'''< |
+ | :*'''<code>[0-9]\\{4\\}</code>''' - matches a 4-digit number for the first year in the notice |
− | :*'''< |
+ | :*'''<code>\\(-".strftime("%Y")."\\)\\@!</code>''' - don't match on up-to-date notices |
− | *'''< |
+ | *'''<code>#s#</code>''' - search and replace the outdated year |
− | :*'''< |
+ | :*'''<code>\\([0-9]\\{4\\}\\)</code>''' - match a 4-digit year and give it a backreference |
− | :*'''< |
+ | :*'''<code>\\(-[0-9]\\{4\\}\\)\\?</code>''' - optionally match an ending year, e.g. the "-2006" in "copyright 2000-2006". |
− | :*'''< |
+ | :*'''<code>#\\1-".strftime("%Y")</code>''' - replace the found text with the first year in the copyright, a hyphen, and the current year |
This script will "jump" to the copyright notice whenever it gets updated. If this is not desired, you can put in a command before the g#s# to create a mark and a command after it to jump back to the mark. |
This script will "jump" to the copyright notice whenever it gets updated. If this is not desired, you can put in a command before the g#s# to create a mark and a command after it to jump back to the mark. |
||
Line 49: | Line 50: | ||
<pre> |
<pre> |
||
\ exe '%s:'. |
\ exe '%s:'. |
||
− | \ '\cCOPYRIGHT\s*\%((c)\|& |
+ | \ '\cCOPYRIGHT\s*\%((c)\|©\|&copy;\)\?\s*'. |
\ '\%([0-9]\{4}\(-[0-9]\{4\}\)\?,\s*\)*\zs'. |
\ '\%([0-9]\{4}\(-[0-9]\{4\}\)\?,\s*\)*\zs'. |
||
\ '\('. |
\ '\('. |
||
Line 62: | Line 63: | ||
\ '&, '.strftime("%Y").':e' | |
\ '&, '.strftime("%Y").':e' | |
||
\ exe '%s:'. |
\ exe '%s:'. |
||
− | \ '\cCOPYRIGHT\s*\%((c)\|& |
+ | \ '\cCOPYRIGHT\s*\%((c)\|©\|&copy;\)\?\s*'. |
\ '\%([0-9]\{4}\%(-[0-9]\{4\}\)\?,\s*\)*\zs'. |
\ '\%([0-9]\{4}\%(-[0-9]\{4\}\)\?,\s*\)*\zs'. |
||
\ '\%('.strftime("%Y").'\)\@!\([0-9]\{4\}\)'. |
\ '\%('.strftime("%Y").'\)\@!\([0-9]\{4\}\)'. |
||
Line 74: | Line 75: | ||
===First Pass (needs a comma)=== |
===First Pass (needs a comma)=== |
||
Summary: Replace all lines with a copyright notice, that do NOT end in the previous or current year, with a comma and the current year. |
Summary: Replace all lines with a copyright notice, that do NOT end in the previous or current year, with a comma and the current year. |
||
− | *'''< |
+ | *'''<code>%s:</code>''' - start a "replace in all lines" search, using ':' rather than the customary '/' for clarity. |
− | *'''< |
+ | *'''<code>\cCOPYRIGHT\s*\%((c)\|©\|&copy;\)\?\s*</code>''' - find lines containing the copyright flag (copyright {optional symbol}) as in the simple method. |
− | *'''< |
+ | *'''<code>\%({below}\)*</code>''' - match any number of year ranges followed by commas, but DO NOT use them for backreferences. |
− | :*'''< |
+ | :*'''<code>[0-9]\{4}\(-[0-9]\{4\}\)\?,\s*</code>''' - match a year or year range, followed by a comma and whitespace. |
− | *'''< |
+ | *'''<code>\zs</code>''' - Place the "start of match" at this point. This means that any matched text previous to this point (i.e. the copyright flag, and any year-ranges followed by commas) will NOT be replaced with the replacement text. |
− | *'''< |
+ | *'''<code>\({below}\&{below}\)</code>''' - The parentheses group two "concats" separated by the \&. All concats must match at the same place for the pattern to match. When it matches, it matches the final concat. |
− | :*'''< |
+ | :*'''<code>\%('.strftime("%Y").'\)\@![0-9]\{4\}</code>''' - match any year that is not the current year. |
− | :*'''< |
+ | :*'''<code>\%(-'.strftime("%Y").'\)\@!\%(-[0-9]\{4\}\)\?</code>''' - optionally match any year range ending ("-{year}") to complete the first concat |
− | :*'''< |
+ | :*'''<code>\%([0-9]\{4\}-\)\?</code>''' - optionally match a year range beginning ("-{year}") |
− | :*'''< |
+ | :*'''<code>\%('.(strftime("%Y")-1).'\)\@!\%([0-9]\)\{4\}</code>''' - match any year except for the previous year (note, current year already known NOT to match) to complete the final concat |
− | *'''< |
+ | *'''<code>\ze</code>''' - place the end of the match at this point, so that any text following this point is NOT affected by the replacement. |
− | *'''< |
+ | *'''<code>\%(\%([0-9]\{4\}\)\@!.\)*$</code>''' - match any text that does not contain a year, until the end of the line. This ensures that we captured only the very last year in the line with our final concat, above. |
− | *'''< |
+ | *'''<code>:&, '.strftime("%Y")</code>''' - replace with the entire match (i.e. the last concat above) followed by a comma and the current year. Recall that the match start and end were defined carefully so that only the desired text is replaced. |
− | *'''< |
+ | *'''<code>:e</code>''' - suppress error when no match is found so you don't see error messages when your copyright is up-to-date, and to allow the mapping to continue on to the second pass... |
===Second Pass (can use a hyphen)=== |
===Second Pass (can use a hyphen)=== |
||
Summary: Replace all remaining lines with a copyright notice, that do NOT end in the current year (i.e. they end in the previous year), with a hyphen and the current year. |
Summary: Replace all remaining lines with a copyright notice, that do NOT end in the current year (i.e. they end in the previous year), with a hyphen and the current year. |
||
− | *'''< |
+ | *'''<code>%s:</code>''' - start a "replace in all lines" search, using ':' rather than the customary '/' for clarity. |
− | *'''< |
+ | *'''<code>\cCOPYRIGHT\s*\%((c)\|©\|&copy;\)\?\s*</code>''' - find lines containing the copyright flag (copyright {optional symbol}) as in the simple method. |
− | *'''< |
+ | *'''<code>\%({below}\)*</code>''' - match any number of year ranges followed by commas, but DO NOT use them for backreferences. |
− | :*'''< |
+ | :*'''<code>[0-9]\{4}\(-[0-9]\{4\}\)\?,\s*</code>''' - match a year or year range, followed by a comma and whitespace. |
− | *'''< |
+ | *'''<code>\zs</code>''' - Place the "start of match" at this point. This means that any matched text previous to this point (i.e. the copyright flag, and any year-ranges followed by commas) will NOT be replaced with the replacement text. |
− | *'''< |
+ | *'''<code>\%('.strftime("%Y").'\)\@!\([0-9]\{4\}\)</code>''' - match any year except for the current year, and place it in the first backreference (note the use of \%(\) in every previous grouping) |
− | *'''< |
+ | *'''<code>\%(-'.strftime("%Y").'\)\@!\%(-[0-9]\{4\}\)\?</code>''' - optionally match a year range ending that does NOT include the current year. |
− | *'''< |
+ | *'''<code>\ze</code>''' - place the end of the match at this point, so that any text following this point is NOT affected by the replacement. |
− | *'''< |
+ | *'''<code>\%(\%([0-9]\{4\}\)\@!.\)*$</code>''' - match any text that does not contain a year, until the end of the line. This ensures that we captured only the very last year in the line. |
− | *'''< |
+ | *'''<code>\1-'.strftime("%Y")</code>''' - replace with the first backreference (i.e. the first year in the final year range of the line), followed by a hyphen and the current year. |
− | *'''< |
+ | *'''<code>:e</code>''' - suppress error when no match is found so you don't see error messages whenever your copyright notice is up-to-date. |
===References=== |
===References=== |
||
Line 119: | Line 120: | ||
==See also== |
==See also== |
||
*[[VimTip97|Tip 97 Insert current date or time]] |
*[[VimTip97|Tip 97 Insert current date or time]] |
||
+ | |||
+ | ==Related plugins== |
||
+ | * {{script|id=680|text=ferallastchange.vim}} is an old plugin that searches for a prefix and then updates the timestamp in a certain format. |
||
+ | * {{script|id=371|text=timstamp.vim}} takes pattern and replacement parts like this plugin (the replacement part also supports some custom tokens e.g. for hostname), and is functionally very close. |
||
+ | * {{script|id=923|text=timestamp.vim}} is based on timstamp.vim. It allows only one pattern and feeds the replacement part to strftime(), making it less general. |
||
+ | * {{script|id=3578|text=lastmod.vim}} is a short and simple plugin for replacing one pattern with a timestamp in the first N lines. |
||
+ | * {{script|id=4654|text=AutoAdapt}} automatically adapts information like "last modified" timestamps and copyright notices, or applies any other substitution before each file save. |
||
==Comments== |
==Comments== |
||
− | |||
− | |||
− | ---- |
Latest revision as of 06:52, 12 July 2013
created 2007 · complexity advanced · author Fritzophrenic · version 7.0
Especially when editing source code, there is often a copyright notice embedded in the file. Insert one of the following in a vimrc file to automatically update this copyright notice in ALL FILES when writing them.
Simple Copyright Notices[]
" Automatically update copyright notice with current year autocmd BufWritePre * \ if &modified | \ exe "g#\\cCOPYRIGHT \\(".strftime("%Y")."\\)\\@![0-9]\\{4\\}\\(-".strftime("%Y")."\\)\\@!#s#\\([0-9]\\{4\\}\\)\\(-[0-9]\\{4\\}\\)\\?#\\1-".strftime("%Y") | \ endif
This replaces yyyy or yyyy-yyyy with yyyy-<current year> on any line in the file containing an outdated "COPYRIGHT yyyy" notice (case-insensitive). It only does this if the file has been modified.
An explanation of the command string follows:
g#
- search and replace only on a line matching copyright notice patten. '#' is used rather than the customary '/' to avoid confusion with all the '\' characters.
\\cCOPYRIGHT
- match on text COPYRIGHT followed by a space (case-insensitive)\\(".strftime("%Y")."\\)\\@!
- only match when the entire match does not consist of the current year (i.e. don't update a notice containing only the current year, like "Copyright 2007").[0-9]\\{4\\}
- matches a 4-digit number for the first year in the notice\\(-".strftime("%Y")."\\)\\@!
- don't match on up-to-date notices
#s#
- search and replace the outdated year
\\([0-9]\\{4\\}\\)
- match a 4-digit year and give it a backreference\\(-[0-9]\\{4\\}\\)\\?
- optionally match an ending year, e.g. the "-2006" in "copyright 2000-2006".#\\1-".strftime("%Y")
- replace the found text with the first year in the copyright, a hyphen, and the current year
This script will "jump" to the copyright notice whenever it gets updated. If this is not desired, you can put in a command before the g#s# to create a mark and a command after it to jump back to the mark.
References[]
More Complex Notices[]
The above works great for simple copyrights with just a range of years, but if you need more precise/correct ones (that skip years something was not worked on, e.g. "copyright 2004, 2006-2008") it will fail miserably. Here's what to replace the guts with, instead of the g#s## command above:
\ exe '%s:'. \ '\cCOPYRIGHT\s*\%((c)\|©\|©\)\?\s*'. \ '\%([0-9]\{4}\(-[0-9]\{4\}\)\?,\s*\)*\zs'. \ '\('. \ '\%('.strftime("%Y").'\)\@![0-9]\{4\}'. \ '\%(-'.strftime("%Y").'\)\@!\%(-[0-9]\{4\}\)\?'. \ '\&'. \ '\%([0-9]\{4\}-\)\?'. \ '\%('.(strftime("%Y")-1).'\)\@!'. \ '\%([0-9]\)\{4\}'. \ '\)'. \ '\ze\%(\%([0-9]\{4\}\)\@!.\)*$:'. \ '&, '.strftime("%Y").':e' | \ exe '%s:'. \ '\cCOPYRIGHT\s*\%((c)\|©\|©\)\?\s*'. \ '\%([0-9]\{4}\%(-[0-9]\{4\}\)\?,\s*\)*\zs'. \ '\%('.strftime("%Y").'\)\@!\([0-9]\{4\}\)'. \ '\%(-'.strftime("%Y").'\)\@!\%(-[0-9]\{4\}\)\?'. \ '\ze\%(\%([0-9]\{4\}\)\@!.\)*$:'. \ '\1-'.strftime("%Y").':e' |
This makes two passes, one to update years that it needs a comma for, the next to update years it can use a hyphen for.
First Pass (needs a comma)[]
Summary: Replace all lines with a copyright notice, that do NOT end in the previous or current year, with a comma and the current year.
%s:
- start a "replace in all lines" search, using ':' rather than the customary '/' for clarity.\cCOPYRIGHT\s*\%((c)\|©\|©\)\?\s*
- find lines containing the copyright flag (copyright {optional symbol}) as in the simple method.\%({below}\)*
- match any number of year ranges followed by commas, but DO NOT use them for backreferences.
[0-9]\{4}\(-[0-9]\{4\}\)\?,\s*
- match a year or year range, followed by a comma and whitespace.
\zs
- Place the "start of match" at this point. This means that any matched text previous to this point (i.e. the copyright flag, and any year-ranges followed by commas) will NOT be replaced with the replacement text.\({below}\&{below}\)
- The parentheses group two "concats" separated by the \&. All concats must match at the same place for the pattern to match. When it matches, it matches the final concat.
\%('.strftime("%Y").'\)\@![0-9]\{4\}
- match any year that is not the current year.\%(-'.strftime("%Y").'\)\@!\%(-[0-9]\{4\}\)\?
- optionally match any year range ending ("-{year}") to complete the first concat\%([0-9]\{4\}-\)\?
- optionally match a year range beginning ("-{year}")\%('.(strftime("%Y")-1).'\)\@!\%([0-9]\)\{4\}
- match any year except for the previous year (note, current year already known NOT to match) to complete the final concat
\ze
- place the end of the match at this point, so that any text following this point is NOT affected by the replacement.\%(\%([0-9]\{4\}\)\@!.\)*$
- match any text that does not contain a year, until the end of the line. This ensures that we captured only the very last year in the line with our final concat, above.:&, '.strftime("%Y")
- replace with the entire match (i.e. the last concat above) followed by a comma and the current year. Recall that the match start and end were defined carefully so that only the desired text is replaced.:e
- suppress error when no match is found so you don't see error messages when your copyright is up-to-date, and to allow the mapping to continue on to the second pass...
Second Pass (can use a hyphen)[]
Summary: Replace all remaining lines with a copyright notice, that do NOT end in the current year (i.e. they end in the previous year), with a hyphen and the current year.
%s:
- start a "replace in all lines" search, using ':' rather than the customary '/' for clarity.\cCOPYRIGHT\s*\%((c)\|©\|©\)\?\s*
- find lines containing the copyright flag (copyright {optional symbol}) as in the simple method.\%({below}\)*
- match any number of year ranges followed by commas, but DO NOT use them for backreferences.
[0-9]\{4}\(-[0-9]\{4\}\)\?,\s*
- match a year or year range, followed by a comma and whitespace.
\zs
- Place the "start of match" at this point. This means that any matched text previous to this point (i.e. the copyright flag, and any year-ranges followed by commas) will NOT be replaced with the replacement text.\%('.strftime("%Y").'\)\@!\([0-9]\{4\}\)
- match any year except for the current year, and place it in the first backreference (note the use of \%(\) in every previous grouping)\%(-'.strftime("%Y").'\)\@!\%(-[0-9]\{4\}\)\?
- optionally match a year range ending that does NOT include the current year.\ze
- place the end of the match at this point, so that any text following this point is NOT affected by the replacement.\%(\%([0-9]\{4\}\)\@!.\)*$
- match any text that does not contain a year, until the end of the line. This ensures that we captured only the very last year in the line.\1-'.strftime("%Y")
- replace with the first backreference (i.e. the first year in the final year range of the line), followed by a hyphen and the current year.:e
- suppress error when no match is found so you don't see error messages whenever your copyright notice is up-to-date.
References[]
Take-Away Lessons[]
- Use the 'g' command or \zs and \ze atoms to carefully limit your 's' commands to certain lines, and to simplify your replacement text for 's' commands.
- Use \%(\) for grouping whenever you don't need the group for a back-reference to simplify things and provide some speedup.
- Zero-width matches such as \@! are very powerful - familiarize yourself with them.
- Complicated regular expressions can be made far less complex by splitting them into sections and working on each section independently.
- Familiarize yourself with the definition of a pattern (:help /pattern) and its constituent components (like branches - :help /branch). Using these components fully can simplify your patterns or even make possible things that are not possible otherwise.
See also[]
Related plugins[]
- ferallastchange.vim is an old plugin that searches for a prefix and then updates the timestamp in a certain format.
- timstamp.vim takes pattern and replacement parts like this plugin (the replacement part also supports some custom tokens e.g. for hostname), and is functionally very close.
- timestamp.vim is based on timstamp.vim. It allows only one pattern and feeds the replacement part to strftime(), making it less general.
- lastmod.vim is a short and simple plugin for replacing one pattern with a timestamp in the first N lines.
- AutoAdapt automatically adapts information like "last modified" timestamps and copyright notices, or applies any other substitution before each file save.