bluecloth 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. data/ChangeLog +629 -0
  2. data/LICENSE +27 -0
  3. data/LICENSE.discount +47 -0
  4. data/README +71 -0
  5. data/Rakefile +319 -0
  6. data/Rakefile.local +63 -0
  7. data/bin/bluecloth +84 -0
  8. data/ext/VERSION +1 -0
  9. data/ext/amalloc.h +29 -0
  10. data/ext/bluecloth.c +373 -0
  11. data/ext/config.h +47 -0
  12. data/ext/cstring.h +73 -0
  13. data/ext/docheader.c +43 -0
  14. data/ext/extconf.rb +45 -0
  15. data/ext/generate.c +1387 -0
  16. data/ext/markdown.c +939 -0
  17. data/ext/markdown.h +135 -0
  18. data/ext/mkdio.c +241 -0
  19. data/ext/mkdio.h +66 -0
  20. data/ext/resource.c +169 -0
  21. data/ext/version.c +28 -0
  22. data/lib/bluecloth.rb +148 -0
  23. data/rake/191_compat.rb +26 -0
  24. data/rake/dependencies.rb +76 -0
  25. data/rake/helpers.rb +412 -0
  26. data/rake/manual.rb +782 -0
  27. data/rake/packaging.rb +116 -0
  28. data/rake/publishing.rb +321 -0
  29. data/rake/rdoc.rb +40 -0
  30. data/rake/style.rb +62 -0
  31. data/rake/svn.rb +639 -0
  32. data/rake/testing.rb +204 -0
  33. data/rake/verifytask.rb +64 -0
  34. data/rake/win32.rb +186 -0
  35. data/spec/bluecloth/101_changes_spec.rb +141 -0
  36. data/spec/bluecloth/autolinks_spec.rb +49 -0
  37. data/spec/bluecloth/blockquotes_spec.rb +143 -0
  38. data/spec/bluecloth/code_spans_spec.rb +164 -0
  39. data/spec/bluecloth/emphasis_spec.rb +164 -0
  40. data/spec/bluecloth/entities_spec.rb +65 -0
  41. data/spec/bluecloth/hrules_spec.rb +90 -0
  42. data/spec/bluecloth/images_spec.rb +92 -0
  43. data/spec/bluecloth/inline_html_spec.rb +238 -0
  44. data/spec/bluecloth/links_spec.rb +171 -0
  45. data/spec/bluecloth/lists_spec.rb +294 -0
  46. data/spec/bluecloth/paragraphs_spec.rb +75 -0
  47. data/spec/bluecloth/titles_spec.rb +305 -0
  48. data/spec/bluecloth_spec.rb +209 -0
  49. data/spec/bugfix_spec.rb +123 -0
  50. data/spec/contributions_spec.rb +85 -0
  51. data/spec/data/antsugar.txt +34 -0
  52. data/spec/data/markdowntest/Amps and angle encoding.html +17 -0
  53. data/spec/data/markdowntest/Amps and angle encoding.text +21 -0
  54. data/spec/data/markdowntest/Auto links.html +18 -0
  55. data/spec/data/markdowntest/Auto links.text +13 -0
  56. data/spec/data/markdowntest/Backslash escapes.html +118 -0
  57. data/spec/data/markdowntest/Backslash escapes.text +120 -0
  58. data/spec/data/markdowntest/Blockquotes with code blocks.html +15 -0
  59. data/spec/data/markdowntest/Blockquotes with code blocks.text +11 -0
  60. data/spec/data/markdowntest/Code Blocks.html +18 -0
  61. data/spec/data/markdowntest/Code Blocks.text +14 -0
  62. data/spec/data/markdowntest/Code Spans.html +5 -0
  63. data/spec/data/markdowntest/Code Spans.text +5 -0
  64. data/spec/data/markdowntest/Hard-wrapped paragraphs with list-like lines.html +8 -0
  65. data/spec/data/markdowntest/Hard-wrapped paragraphs with list-like lines.text +8 -0
  66. data/spec/data/markdowntest/Horizontal rules.html +71 -0
  67. data/spec/data/markdowntest/Horizontal rules.text +67 -0
  68. data/spec/data/markdowntest/Inline HTML (Advanced).html +15 -0
  69. data/spec/data/markdowntest/Inline HTML (Advanced).text +15 -0
  70. data/spec/data/markdowntest/Inline HTML (Simple).html +72 -0
  71. data/spec/data/markdowntest/Inline HTML (Simple).text +69 -0
  72. data/spec/data/markdowntest/Inline HTML comments.html +13 -0
  73. data/spec/data/markdowntest/Inline HTML comments.text +13 -0
  74. data/spec/data/markdowntest/Links, inline style.html +11 -0
  75. data/spec/data/markdowntest/Links, inline style.text +12 -0
  76. data/spec/data/markdowntest/Links, reference style.html +52 -0
  77. data/spec/data/markdowntest/Links, reference style.text +71 -0
  78. data/spec/data/markdowntest/Links, shortcut references.html +9 -0
  79. data/spec/data/markdowntest/Links, shortcut references.text +20 -0
  80. data/spec/data/markdowntest/Literal quotes in titles.html +3 -0
  81. data/spec/data/markdowntest/Literal quotes in titles.text +7 -0
  82. data/spec/data/markdowntest/Markdown Documentation - Basics.html +314 -0
  83. data/spec/data/markdowntest/Markdown Documentation - Basics.text +306 -0
  84. data/spec/data/markdowntest/Markdown Documentation - Syntax.html +942 -0
  85. data/spec/data/markdowntest/Markdown Documentation - Syntax.text +888 -0
  86. data/spec/data/markdowntest/Nested blockquotes.html +9 -0
  87. data/spec/data/markdowntest/Nested blockquotes.text +5 -0
  88. data/spec/data/markdowntest/Ordered and unordered lists.html +148 -0
  89. data/spec/data/markdowntest/Ordered and unordered lists.text +131 -0
  90. data/spec/data/markdowntest/Strong and em together.html +7 -0
  91. data/spec/data/markdowntest/Strong and em together.text +7 -0
  92. data/spec/data/markdowntest/Tabs.html +25 -0
  93. data/spec/data/markdowntest/Tabs.text +21 -0
  94. data/spec/data/markdowntest/Tidyness.html +8 -0
  95. data/spec/data/markdowntest/Tidyness.text +5 -0
  96. data/spec/data/ml-announce.txt +17 -0
  97. data/spec/data/re-overflow.txt +67 -0
  98. data/spec/data/re-overflow2.txt +281 -0
  99. data/spec/lib/constants.rb +5 -0
  100. data/spec/lib/helpers.rb +137 -0
  101. data/spec/lib/matchers.rb +235 -0
  102. data/spec/markdowntest_spec.rb +76 -0
  103. metadata +305 -0
@@ -0,0 +1,62 @@
1
+ #
2
+ # Style Fixup Rake Tasks
3
+ # $Id: style.rb 10 2008-07-18 15:52:48Z deveiant $
4
+ #
5
+ # Authors:
6
+ # * Michael Granger <ged@FaerieMUD.org>
7
+ #
8
+
9
+
10
+ ### Coding style checks and fixes
11
+ namespace :style do
12
+
13
+ BLANK_LINE = /^\s*$/
14
+ GOOD_INDENT = /^(\t\s*)?\S/
15
+
16
+ # A list of the files that have legitimate leading whitespace, etc.
17
+ PROBLEM_FILES = [ SPECDIR + 'config_spec.rb' ]
18
+
19
+ desc "Check source files for inconsistent indent and fix them"
20
+ task :fix_indent do
21
+ files = LIB_FILES + SPEC_FILES
22
+
23
+ badfiles = Hash.new {|h,k| h[k] = [] }
24
+
25
+ trace "Checking files for indentation"
26
+ files.each do |file|
27
+ if PROBLEM_FILES.include?( file )
28
+ trace " skipping problem file #{file}..."
29
+ next
30
+ end
31
+
32
+ trace " #{file}"
33
+ linecount = 0
34
+ file.each_line do |line|
35
+ linecount += 1
36
+
37
+ # Skip blank lines
38
+ next if line =~ BLANK_LINE
39
+
40
+ # If there's a line with incorrect indent, note it and skip to the
41
+ # next file
42
+ if line !~ GOOD_INDENT
43
+ trace " Bad line %d: %p" % [ linecount, line ]
44
+ badfiles[file] << [ linecount, line ]
45
+ end
46
+ end
47
+ end
48
+
49
+ if badfiles.empty?
50
+ log "No indentation problems found."
51
+ else
52
+ log "Found incorrect indent in #{badfiles.length} files:\n "
53
+ badfiles.each do |file, badlines|
54
+ log " #{file}:\n" +
55
+ " " + badlines.collect {|badline| "%5d: %p" % badline }.join( "\n " )
56
+ end
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+
@@ -0,0 +1,639 @@
1
+ #
2
+ # Subversion Rake Tasks
3
+ # $Id: svn.rb 95 2009-04-02 15:21:01Z deveiant $
4
+ #
5
+ # Authors:
6
+ # * Michael Granger <ged@FaerieMUD.org>
7
+ #
8
+
9
+ require 'pp'
10
+ require 'yaml'
11
+ require 'date'
12
+ require 'time'
13
+ require 'abbrev'
14
+
15
+ # Strftime format for tags/releases
16
+ TAG_TIMESTAMP_FORMAT = '%Y%m%d-%H%M%S'
17
+ TAG_TIMESTAMP_PATTERN = /\d{4}\d{2}\d{2}-\d{6}/
18
+
19
+ RELEASE_VERSION_PATTERN = /\d+\.\d+\.\d+/
20
+
21
+ DEFAULT_EDITOR = 'vi'
22
+ DEFAULT_KEYWORDS = %w[Date Rev Author URL Id]
23
+ KEYWORDED_FILEDIRS = %w[applets spec bin etc ext experiments examples lib misc docs]
24
+ KEYWORDED_FILEPATTERN = /
25
+ ^(?:
26
+ (?:meta)?rakefile.* # Rakefiles
27
+ |
28
+ .*\.(?:rb|c|h|js|html|css|template|erb|page) # Source file extensions
29
+ |
30
+ readme|install|todo
31
+ )$/ix
32
+
33
+ COMMIT_MSG_FILE = 'commit-msg.txt'
34
+
35
+ SVN_TRUNK_DIR = 'trunk' unless defined?( SVN_TRUNK_DIR )
36
+ SVN_RELEASES_DIR = 'branches' unless defined?( SVN_RELEASES_DIR )
37
+ SVN_BRANCHES_DIR = 'branches' unless defined?( SVN_BRANCHES_DIR )
38
+ SVN_TAGS_DIR = 'tags' unless defined?( SVN_TAGS_DIR )
39
+
40
+ FILE_INDENT = " " * 12
41
+ LOG_INDENT = " " * 3
42
+
43
+
44
+
45
+ ###
46
+ ### Subversion-specific Helpers
47
+ ###
48
+
49
+ ### Return a new tag for the given time
50
+ def make_new_tag( time=Time.now )
51
+ return time.strftime( TAG_TIMESTAMP_FORMAT )
52
+ end
53
+
54
+
55
+ ### Get the subversion information for the current working directory as
56
+ ### a hash.
57
+ def get_svn_info( dir='.' )
58
+ return {} unless File.directory?( File.join(dir, '.svn') )
59
+ info = IO.read( '|-' ) or exec 'svn', 'info', dir.to_s
60
+ return YAML.load( info ) # 'svn info' outputs valid YAML! Yay!
61
+ rescue NotImplementedError
62
+ trace "No fork(), proceeding without svn info..."
63
+ return {}
64
+ end
65
+
66
+
67
+ ### Get a list of the objects registered with subversion under the specified directory and
68
+ ### return them as an Array of Pathame objects.
69
+ def get_svn_filelist( dir='.' )
70
+ list = IO.read( '|-' ) or exec 'svn', 'st', '-v', '--ignore-externals', dir
71
+
72
+ # Split into lines, filter out the unknowns, and grab the filenames as Pathnames
73
+ # :FIXME: This will break if we ever put in a file with spaces in its name. This
74
+ # will likely be the least of our worries if we do so, however, so it's not worth
75
+ # the additional complexity to make it handle that case. If we do need that, there's
76
+ # always the --xml output for 'svn st'...
77
+ return list.split( $/ ).
78
+ reject {|line| line =~ /^(\?|(\s*|--- .*)$)/ }.
79
+ collect {|fn| Pathname(fn[/\S+$/]) }
80
+ end
81
+
82
+ ### Return the URL to the repository root for the specified +dir+.
83
+ def get_svn_repo_root( dir='.' )
84
+ info = get_svn_info( dir )
85
+ return info['Repository Root']
86
+ end
87
+
88
+
89
+ ### Return the Subversion URL to the given +dir+.
90
+ def get_svn_url( dir='.' )
91
+ info = get_svn_info( dir )
92
+ return info['URL']
93
+ end
94
+
95
+
96
+ ### Return the path of the specified +dir+ under the svn root of the
97
+ ### checkout.
98
+ def get_svn_path( dir='.' )
99
+ root = get_svn_repo_root( dir )
100
+ url = get_svn_url( dir )
101
+
102
+ return url.sub( root + '/', '' )
103
+ end
104
+
105
+
106
+ ### Return the keywords for the specified array of +files+ as a Hash keyed by filename.
107
+ def get_svn_keyword_map( *files )
108
+ files.flatten!
109
+ files.push( '.' ) if files.empty?
110
+
111
+ cmd = ['svn', 'pg', 'svn:keywords', *files]
112
+
113
+ # trace "Executing: svn pg svn:keywords " + files.join(' ')
114
+ output = IO.read( '|-' ) or exec( 'svn', 'pg', 'svn:keywords', *files )
115
+
116
+ kwmap = {}
117
+ output.split( "\n" ).each do |line|
118
+ next if line !~ /\s+-\s+/
119
+ path, keywords = line.split( /\s+-\s+/, 2 )
120
+ kwmap[ path ] = keywords.split
121
+ end
122
+
123
+ return kwmap
124
+ end
125
+
126
+
127
+ ### Return the latest revision number of the specified +dir+ as an Integer.
128
+ def get_svn_rev( dir='.' )
129
+ info = get_svn_info( dir )
130
+ return info['Revision']
131
+ end
132
+
133
+
134
+ ### Return the latest revision number of the specified +dir+ as an Integer.
135
+ def get_last_changed_rev( dir='.' )
136
+ info = get_svn_info( dir )
137
+ return info['Last Changed Rev']
138
+ end
139
+
140
+
141
+ ### Return a list of the entries at the specified Subversion url. If
142
+ ### no +url+ is specified, it will default to the list in the URL
143
+ ### corresponding to the current working directory.
144
+ def svn_ls( url=nil )
145
+ url ||= get_svn_url()
146
+ list = IO.read( '|-' ) or exec 'svn', 'ls', url
147
+
148
+ trace 'svn ls of %s: %p' % [url, list] if $trace
149
+
150
+ return [] if list.nil? || list.empty?
151
+ return list.split( $INPUT_RECORD_SEPARATOR )
152
+ end
153
+
154
+
155
+ ### Return the URL of the latest timestamp in the tags directory.
156
+ def get_latest_svn_timestamp_tag
157
+ rooturl = get_svn_repo_root()
158
+ tagsurl = rooturl + "/#{SVN_TAGS_DIR}"
159
+
160
+ tags = svn_ls( tagsurl ).grep( TAG_TIMESTAMP_PATTERN ).sort
161
+ return nil if tags.nil? || tags.empty?
162
+ return tagsurl + '/' + tags.last
163
+ end
164
+
165
+
166
+ ### Get a subversion diff of the specified targets and return it. If no targets are
167
+ ### specified, the current directory will be diffed instead.
168
+ def get_svn_diff( *targets )
169
+ targets << BASEDIR if targets.empty?
170
+ trace "Getting svn diff for targets: %p" % [targets]
171
+ log = IO.read( '|-' ) or exec 'svn', 'diff', *(targets.flatten)
172
+
173
+ return log
174
+ end
175
+
176
+
177
+ ### Get a subversion status as an Array of tuples of the form:
178
+ ### [ <status>, <path> ]
179
+ def get_svn_status( *targets )
180
+ targets << BASEDIR if targets.empty?
181
+ trace "Getting svn status for targets: %p" % [targets]
182
+ status = IO.read( '|-' ) or exec 'svn', 'st', '--ignore-externals', *(targets.flatten)
183
+ entries = status.split( /\n/ ).
184
+ select {|line| line !~ /^(\s*|--- .*)$/ }.
185
+ collect do |line|
186
+ flag, path = line.strip.split( /\s+/, 2 )
187
+ [ flag, Pathname.new(path) ]
188
+ end
189
+
190
+ return entries
191
+ end
192
+
193
+
194
+ ### Return the URL of the latest timestamp in the tags directory.
195
+ def get_latest_release_tag
196
+ rooturl = get_svn_repo_root()
197
+ releaseurl = rooturl + "/#{SVN_RELEASES_DIR}"
198
+
199
+ tags = svn_ls( releaseurl ).grep( RELEASE_VERSION_PATTERN ).sort_by do |tag|
200
+ tag[RELEASE_VERSION_PATTERN].split('.').collect {|i| Integer(i) }
201
+ end
202
+ return nil if tags.empty?
203
+
204
+ return releaseurl + '/' + tags.last
205
+ end
206
+
207
+
208
+ ### Return the names of all existing branches.
209
+ def get_branch_names
210
+ rooturl = get_svn_repo_root()
211
+ branchesurl = rooturl + "/#{SVN_BRANCHES_DIR}"
212
+
213
+ return svn_ls( branchesurl )
214
+ end
215
+
216
+
217
+ ### Extract a diff from the specified subversion working +dir+ and return it.
218
+ def make_svn_commit_log( dir='.' )
219
+ diff = IO.read( '|-' ) or exec 'svn', 'diff'
220
+ fail "No differences." if diff.empty?
221
+
222
+ return diff
223
+ end
224
+
225
+
226
+ ### Extract the svn log from the specified subversion working +dir+,
227
+ ### starting from rev +start+ and ending with rev +finish+, and return it.
228
+ def make_svn_log( dir='.', start='PREV', finish='HEAD' )
229
+ trace "svn log -r#{start}:#{finish} #{dir}"
230
+ log = IO.read( '|-' ) or exec 'svn', 'log', "-r#{start}:#{finish}", dir
231
+ fail "No log between #{start} and #{finish}." if log.empty?
232
+
233
+ return log
234
+ end
235
+
236
+
237
+ ### Extract the verbose XML svn log from the specified subversion working +dir+,
238
+ ### starting from rev +start+ and ending with rev +finish+, and return it.
239
+ def make_xml_svn_log( dir='.', start='PREV', finish='HEAD' )
240
+ trace "svn log --xml --verbose -r#{start}:#{finish} #{dir}"
241
+ log = IO.read( '|-' ) or exec 'svn', 'log', '--verbose', '--xml', "-r#{start}:#{finish}", dir
242
+ fail "No log between #{start} and #{finish}." if log.empty?
243
+
244
+ return log
245
+ end
246
+
247
+
248
+ ### Create a changelog from the subversion log of the specified +dir+ and return it.
249
+ def make_svn_changelog( dir='.' )
250
+ require 'xml/libxml'
251
+
252
+ changelog = ''
253
+ path_prefix = '/' + get_svn_path( dir ) + '/'
254
+
255
+ xmllog = make_xml_svn_log( dir, 0 )
256
+
257
+ parser = XML::Parser.string( xmllog )
258
+ root = parser.parse.root
259
+ root.find( '//log/logentry' ).to_a.reverse.each do |entry|
260
+ trace "Making a changelog entry for r%s" % [ entry['revision'] ]
261
+
262
+ added = []
263
+ deleted = []
264
+ changed = []
265
+
266
+ entry.find( 'paths/path').each do |path|
267
+ pathname = path.content
268
+ pathname.sub!( path_prefix , '' ) if pathname.count('/') > 1
269
+
270
+ case path['action']
271
+ when 'A', 'R'
272
+ if path['copyfrom-path']
273
+ verb = path['action'] == 'A' ? 'renamed' : 'copied'
274
+ added << "%s\n#{FILE_INDENT}-> #{verb} from %s@r%s" % [
275
+ pathname,
276
+ path['copyfrom-path'],
277
+ path['copyfrom-rev'],
278
+ ]
279
+ else
280
+ added << "%s (new)" % [ pathname ]
281
+ end
282
+
283
+ when 'M'
284
+ changed << pathname
285
+
286
+ when 'D'
287
+ deleted << pathname
288
+
289
+ else
290
+ log "Unknown action %p in rev %d" % [ path['action'], entry['revision'] ]
291
+ end
292
+
293
+ end
294
+
295
+ date = Time.parse( entry.find_first('date').content )
296
+
297
+ # cvs2svn doesn't set 'author'
298
+ author = 'unknown'
299
+ if entry.find_first( 'author' )
300
+ author = entry.find_first( 'author' ).content
301
+ end
302
+
303
+ msg = entry.find_first( 'msg' ).content
304
+ rev = entry['revision']
305
+
306
+ changelog << "-- #{date.rfc2822} by #{author} (r#{rev}) -----\n"
307
+ changelog << " Added: " << humanize_file_list(added) << "\n" unless added.empty?
308
+ changelog << " Changed: " << humanize_file_list(changed) << "\n" unless changed.empty?
309
+ changelog << " Deleted: " << humanize_file_list(deleted) << "\n" unless deleted.empty?
310
+ changelog << "\n"
311
+
312
+ indent = msg[/^(\s*)/] + LOG_INDENT
313
+
314
+ changelog << indent << msg.strip.gsub(/\n\s*/m, "\n#{indent}")
315
+ changelog << "\n\n\n"
316
+ end
317
+
318
+ return changelog
319
+ end
320
+
321
+
322
+ ### Returns a human-scannable file list by joining and truncating the list if it's too long.
323
+ def humanize_file_list( list, indent=FILE_INDENT )
324
+ listtext = list[0..5].join( "\n#{indent}" )
325
+ if list.length > 5
326
+ listtext << " (and %d other/s)" % [ list.length - 5 ]
327
+ end
328
+
329
+ return listtext
330
+ end
331
+
332
+
333
+ ### Add the list of +pathnames+ to the svn:ignore list.
334
+ def svn_ignore_files( *pathnames )
335
+ pathnames.flatten!
336
+
337
+ map = pathnames.inject({}) do |map,path|
338
+ map[ path.dirname ] ||= []
339
+ map[ path.dirname ] << path.basename
340
+ map
341
+ end
342
+
343
+ trace "Ignoring %d files in %d directories." % [ pathnames.length, map.length ]
344
+
345
+ map.each do |dir, files|
346
+ trace " %s: %p" % [ dir, files ]
347
+ io = open( '|-' ) or exec 'svn', 'pg', 'svn:ignore', dir
348
+ ignorelist = io.read.strip
349
+ ignorelist << "\n" << files.join("\n")
350
+ system 'svn', 'ps', 'svn:ignore', ignorelist, dir
351
+ end
352
+ end
353
+
354
+
355
+ ### Delete the files in the given +filelist+ after confirming with the user.
356
+ def delete_extra_files( filelist )
357
+ description = humanize_file_list( filelist, ' ' )
358
+ log "Files to delete:\n ", description
359
+ ask_for_confirmation( "Really delete them?", false ) do
360
+ filelist.each do |f|
361
+ rm_rf( f, :verbose => true )
362
+ end
363
+ end
364
+ end
365
+
366
+
367
+
368
+ ###
369
+ ### Tasks
370
+ ###
371
+
372
+ desc "Subversion tasks"
373
+ namespace :svn do
374
+
375
+ desc "Copy the HEAD revision of the current #{SVN_TRUNK_DIR}/ to #{SVN_TAGS_DIR}/ with a " +
376
+ "current timestamp."
377
+ task :tag do
378
+ svninfo = get_svn_info()
379
+ tag = make_new_tag()
380
+ svntrunk = svninfo['Repository Root'] + "/#{SVN_TRUNK_DIR}"
381
+ svntagdir = svninfo['Repository Root'] + "/#{SVN_TAGS_DIR}"
382
+ svntag = svntagdir + '/' + tag
383
+
384
+ desc = "Tagging trunk as #{svntag}"
385
+ ask_for_confirmation( desc ) do
386
+ msg = prompt_with_default( "Commit log: ", "Tagging for code push" )
387
+ run 'svn', 'cp', '-m', msg, svntrunk, svntag
388
+ end
389
+ end
390
+
391
+
392
+ desc "Copy the HEAD revision of the current #{SVN_TRUNK_DIR}/ to #{SVN_BRANCHES_DIR} with a " +
393
+ "user-specified name."
394
+ task :branch, [:name] do |task, args|
395
+ unless args.name
396
+ args.name = prompt( "Branch name" ) or abort
397
+ end
398
+
399
+ svninfo = get_svn_info()
400
+ svntrunk = Pathname.new( svninfo['Repository Root'] ) + SVN_TRUNK_DIR
401
+ svnbranchdir = Pathname.new( svninfo['Repository Root'] ) + SVN_BRANCHES_DIR
402
+ svnbranch = svnbranchdir + args.name
403
+
404
+ desc = "Making a new branch: #{svnbranch}"
405
+ ask_for_confirmation( desc ) do
406
+ msg = prompt_with_default( "Commit log: ", "Making a '#{args.name}' branch" )
407
+ run 'svn', 'cp', '-m', msg, svntrunk, svnbranch
408
+ ask_for_confirmation( "Switch to the new branch?", false ) do
409
+ run 'svn', 'sw', svnbranch
410
+ end
411
+ end
412
+ end
413
+
414
+
415
+ desc "Switch the working copy to the named branch"
416
+ task :switch, [:name] do |task, args|
417
+ branches = get_branch_names().collect {|name| name.chomp('/') }
418
+
419
+ unless args.name
420
+ log "Branches are:\n" + branches.collect {|br| " #{br}" }.join( "\n" )
421
+
422
+ begin
423
+ oldproc = Readline.completion_proc
424
+ abbrev = branches.abbrev
425
+ Readline.completion_proc = lambda{|string| abbrev[string] }
426
+
427
+ name = prompt( "Branch to switch to" ) or abort
428
+ args.with_defaults( :name => name )
429
+ ensure
430
+ Readline.completion_proc = oldproc unless oldproc.nil?
431
+ end
432
+ end
433
+
434
+ svninfo = get_svn_info()
435
+ abort "Branch '#{args.name}' does not exist" unless branches.include?( args.name )
436
+ branchuri = Pathname.new( svninfo['Repository Root'] ) + SVN_BRANCHES_DIR + args.name
437
+ run 'svn', 'sw', branchuri
438
+ end
439
+ task :sw => :switch
440
+
441
+
442
+ desc "Switch to the trunk if the working copy isn't there already."
443
+ task :trunk do
444
+ svninfo = get_svn_info()
445
+ svntrunk = Pathname.new( svninfo['Repository Root'] ) + SVN_TRUNK_DIR
446
+
447
+ if svninfo['URL'] != svntrunk.to_s
448
+ log "Switching to #{svntrunk}"
449
+ run 'svn', 'sw', svntrunk
450
+ else
451
+ log "You are already on trunk (#{svntrunk})"
452
+ end
453
+ end
454
+
455
+
456
+ desc "Copy the most recent tag to #{SVN_RELEASES_DIR}/#{PKG_VERSION}"
457
+ task :release do
458
+ last_tag = get_latest_svn_timestamp_tag()
459
+ svninfo = get_svn_info()
460
+ svnroot = Pathname.new( svninfo['Repository Root'] )
461
+ svntrunk = svnroot + SVN_TRUNK_DIR
462
+ svnrel = svnroot + SVN_RELEASES_DIR
463
+ release = PKG_VERSION
464
+ svnrelease = svnrel + release
465
+
466
+ unless svn_ls( svnrel.dirname ).include?( svnrel.basename.to_s + '/' )
467
+ log "Releases path #{svnrel} does not exist."
468
+ ask_for_confirmation( "To continue I'll need to create it." ) do
469
+ run 'svn', 'mkdir', svnrel, '-m', 'Creating releases/ directory'
470
+ end
471
+ else
472
+ trace "Found release dir #{svnrel}"
473
+ end
474
+
475
+ releases = svn_ls( svnrel ).collect {|name| name.sub(%r{/$}, '') }
476
+ trace "Releases: %p" % [releases]
477
+ if releases.include?( release )
478
+ error "Version #{release} already has a branch (#{svnrelease}). Did you mean " +
479
+ "to increment the version in #{VERSION_FILE}?"
480
+ fail
481
+ else
482
+ trace "No #{release} version currently exists"
483
+ end
484
+
485
+ desc = "Tagging trunk as #{svnrelease}..."
486
+ ask_for_confirmation( desc ) do
487
+ msg = prompt_with_default( "Commit log: ", "Branching for release" )
488
+ run 'svn', 'cp', '-m', msg, svntrunk, svnrelease
489
+ end
490
+ end
491
+
492
+ ### Task for debugging the #get_target_args helper
493
+ task :show_targets do
494
+ $stdout.puts "Targets from ARGV (%p): %p" % [ARGV, get_target_args()]
495
+ end
496
+
497
+
498
+ desc "Generate a commit log"
499
+ task :commitlog => [COMMIT_MSG_FILE]
500
+
501
+ desc "Show the (pre-edited) commit log for the current directory"
502
+ task :show_commitlog do
503
+ puts make_svn_commit_log()
504
+ end
505
+
506
+
507
+ file COMMIT_MSG_FILE do
508
+ diff = make_svn_commit_log()
509
+
510
+ File.open( COMMIT_MSG_FILE, File::WRONLY|File::EXCL|File::CREAT ) do |fh|
511
+ fh.print( diff )
512
+ end
513
+
514
+ editor = ENV['EDITOR'] || ENV['VISUAL'] || DEFAULT_EDITOR
515
+ system editor, COMMIT_MSG_FILE
516
+ unless $?.success?
517
+ fail "Editor exited uncleanly."
518
+ end
519
+ end
520
+
521
+
522
+ desc "Update from Subversion"
523
+ task :update do
524
+ run 'svn', 'up', '--ignore-externals'
525
+ end
526
+
527
+
528
+ desc "Add/ignore any files that are unknown in the working copy"
529
+ task :newfiles do
530
+ log "Checking for new files..."
531
+ entries = get_svn_status()
532
+
533
+ unless entries.empty?
534
+ files_to_add = []
535
+ files_to_ignore = []
536
+ files_to_delete = []
537
+
538
+ entries.find_all {|entry| entry[0] == '?'}.each do |entry|
539
+ action = prompt_with_default( " #{entry[1]}: (a)dd, (i)gnore, (s)kip (d)elete", 's' )
540
+ case action
541
+ when 'a'
542
+ files_to_add << entry[1]
543
+ when 'i'
544
+ files_to_ignore << entry[1]
545
+ when 'd'
546
+ files_to_delete << entry[1]
547
+ end
548
+ end
549
+
550
+ unless files_to_add.empty?
551
+ run 'svn', 'add', *files_to_add
552
+ end
553
+
554
+ unless files_to_ignore.empty?
555
+ svn_ignore_files( *files_to_ignore )
556
+ end
557
+
558
+ unless files_to_delete.empty?
559
+ delete_extra_files( files_to_delete )
560
+ end
561
+ end
562
+ end
563
+ task :add => :newfiles
564
+
565
+
566
+ desc "Check in all the changes in your current working copy"
567
+ task :checkin => ['svn:update', 'svn:newfiles', 'test', 'svn:fix_keywords', COMMIT_MSG_FILE] do
568
+ targets = get_target_args()
569
+ $stderr.puts '---', File.read( COMMIT_MSG_FILE ), '---'
570
+ ask_for_confirmation( "Continue with checkin?" ) do
571
+ run 'svn', 'ci', '-F', COMMIT_MSG_FILE, targets
572
+ rm_f COMMIT_MSG_FILE
573
+ end
574
+ end
575
+ task :commit => :checkin
576
+ task :ci => :checkin
577
+
578
+
579
+ task :clean do
580
+ rm_f COMMIT_MSG_FILE
581
+ end
582
+
583
+
584
+ desc "Check and fix any missing keywords for any files in the project which need them"
585
+ task :fix_keywords do
586
+ log "Checking subversion keywords..."
587
+ paths = get_svn_filelist( BASEDIR ).
588
+ select {|path| path.file? && path.to_s =~ KEYWORDED_FILEPATTERN }
589
+
590
+ trace "Looking at %d paths for keywords:\n %p" % [paths.length, paths]
591
+ kwmap = get_svn_keyword_map( paths )
592
+
593
+ buf = ''
594
+ PP.pp( kwmap, buf, 132 )
595
+ trace "keyword map is: %s" % [buf]
596
+
597
+ files_needing_fixups = paths.find_all do |path|
598
+ (kwmap[path.to_s] & DEFAULT_KEYWORDS) != DEFAULT_KEYWORDS
599
+ end
600
+
601
+ unless files_needing_fixups.empty?
602
+ $stderr.puts "Files needing keyword fixes: ",
603
+ files_needing_fixups.collect {|f|
604
+ " %s: %s" % [f, kwmap[f] ? kwmap[f].join(' ') : "(no keywords)"]
605
+ }
606
+ ask_for_confirmation( "Will add default keywords to these files." ) do
607
+ run 'svn', 'ps', 'svn:keywords', DEFAULT_KEYWORDS.join(' '), *files_needing_fixups
608
+ end
609
+ else
610
+ log "Keywords are all up to date."
611
+ end
612
+ end
613
+
614
+
615
+ task :debug_helpers do
616
+ methods = [
617
+ :get_last_changed_rev,
618
+ :get_latest_release_tag,
619
+ :get_latest_svn_timestamp_tag,
620
+ :get_svn_diff,
621
+ :get_svn_filelist,
622
+ :get_svn_info,
623
+ :get_svn_keyword_map,
624
+ :get_svn_path,
625
+ :get_svn_repo_root,
626
+ :get_svn_rev,
627
+ :get_svn_status,
628
+ :get_svn_url,
629
+ :svn_ls,
630
+ ]
631
+ maxlen = methods.collect {|sym| sym.to_s.length }.max
632
+
633
+ methods.each do |meth|
634
+ res = send( meth )
635
+ puts "%*s => %p" % [ maxlen, colorize(meth.to_s, :cyan), res ]
636
+ end
637
+ end
638
+ end
639
+