codnar 0.1.64

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 (80) hide show
  1. data/ChangeLog +165 -0
  2. data/LICENSE +19 -0
  3. data/README.rdoc +32 -0
  4. data/Rakefile +66 -0
  5. data/bin/codnar-split +5 -0
  6. data/bin/codnar-weave +5 -0
  7. data/codnar.html +10945 -0
  8. data/doc/logo.png +0 -0
  9. data/doc/root.html +22 -0
  10. data/doc/story.markdown +180 -0
  11. data/doc/system.markdown +671 -0
  12. data/lib/codnar.rb +41 -0
  13. data/lib/codnar/application.rb +92 -0
  14. data/lib/codnar/cache.rb +61 -0
  15. data/lib/codnar/data/contents.js +113 -0
  16. data/lib/codnar/data/control_chunks.js +44 -0
  17. data/lib/codnar/data/style.css +95 -0
  18. data/lib/codnar/data/sunlight/README.txt +4 -0
  19. data/lib/codnar/data/sunlight/css-min.js +1 -0
  20. data/lib/codnar/data/sunlight/default.css +236 -0
  21. data/lib/codnar/data/sunlight/javascript-min.js +1 -0
  22. data/lib/codnar/data/sunlight/min.js +1 -0
  23. data/lib/codnar/data/sunlight/ruby-min.js +1 -0
  24. data/lib/codnar/data/yui/README.txt +3 -0
  25. data/lib/codnar/data/yui/base.css +132 -0
  26. data/lib/codnar/data/yui/reset.css +142 -0
  27. data/lib/codnar/formatter.rb +180 -0
  28. data/lib/codnar/grouper.rb +28 -0
  29. data/lib/codnar/gvim.rb +132 -0
  30. data/lib/codnar/hash_extensions.rb +41 -0
  31. data/lib/codnar/markdown.rb +47 -0
  32. data/lib/codnar/merger.rb +138 -0
  33. data/lib/codnar/rake.rb +41 -0
  34. data/lib/codnar/rake/split_task.rb +71 -0
  35. data/lib/codnar/rake/weave_task.rb +59 -0
  36. data/lib/codnar/rdoc.rb +9 -0
  37. data/lib/codnar/reader.rb +121 -0
  38. data/lib/codnar/scanner.rb +216 -0
  39. data/lib/codnar/split.rb +58 -0
  40. data/lib/codnar/split_configurations.rb +367 -0
  41. data/lib/codnar/splitter.rb +32 -0
  42. data/lib/codnar/string_extensions.rb +25 -0
  43. data/lib/codnar/sunlight.rb +17 -0
  44. data/lib/codnar/version.rb +8 -0
  45. data/lib/codnar/weave.rb +58 -0
  46. data/lib/codnar/weave_configurations.rb +48 -0
  47. data/lib/codnar/weaver.rb +105 -0
  48. data/lib/codnar/writer.rb +38 -0
  49. data/test/cache_computations.rb +41 -0
  50. data/test/deep_merge.rb +29 -0
  51. data/test/embed_images.rb +12 -0
  52. data/test/expand_markdown.rb +27 -0
  53. data/test/expand_rdoc.rb +20 -0
  54. data/test/format_code_gvim_configurations.rb +55 -0
  55. data/test/format_code_sunlight_configurations.rb +37 -0
  56. data/test/format_comment_configurations.rb +86 -0
  57. data/test/format_lines.rb +72 -0
  58. data/test/group_lines.rb +31 -0
  59. data/test/gvim_highlight_syntax.rb +49 -0
  60. data/test/identify_chunks.rb +32 -0
  61. data/test/lib/test_with_configurations.rb +15 -0
  62. data/test/merge_lines.rb +133 -0
  63. data/test/rake_tasks.rb +38 -0
  64. data/test/read_chunks.rb +110 -0
  65. data/test/run_application.rb +56 -0
  66. data/test/run_split.rb +38 -0
  67. data/test/run_weave.rb +75 -0
  68. data/test/scan_lines.rb +78 -0
  69. data/test/split_chunk_configurations.rb +55 -0
  70. data/test/split_code.rb +109 -0
  71. data/test/split_code_configurations.rb +73 -0
  72. data/test/split_combined_configurations.rb +114 -0
  73. data/test/split_complex_comment_configurations.rb +73 -0
  74. data/test/split_documentation.rb +92 -0
  75. data/test/split_documentation_configurations.rb +97 -0
  76. data/test/split_simple_comment_configurations.rb +50 -0
  77. data/test/sunlight_highlight_syntax.rb +25 -0
  78. data/test/weave_configurations.rb +144 -0
  79. data/test/write_chunks.rb +28 -0
  80. metadata +363 -0
@@ -0,0 +1,58 @@
1
+ module Codnar
2
+
3
+ # Split application.
4
+ class Split < Application
5
+
6
+ # Run the weaving Codnar application, returning its status.
7
+ def run
8
+ super { split }
9
+ end
10
+
11
+ protected
12
+
13
+ # Split the specified input file into chunks.
14
+ def split
15
+ @configuration = Codnar::Configuration::SPLIT_HTML_DOCUMENTATION if @configuration == {}
16
+ splitter = Splitter.new(@errors, @configuration)
17
+ print(splitter.chunks(ARGV[0]).to_yaml)
18
+ end
19
+
20
+ # Parse remaining command-line file arguments.
21
+ def parse_arguments
22
+ case ARGV.size
23
+ when 1 then return
24
+ when 0 then $stderr.puts("#{$0}: No input file to split")
25
+ else $stderr.puts("#{$0}: Too many input files to split")
26
+ end
27
+ exit(1)
28
+ end
29
+
30
+ # Return the banner line of the help message.
31
+ def banner
32
+ return "codnar-split - Split documentation or code files to chunks."
33
+ end
34
+
35
+ # Return the name and description of any final command-line file arguments.
36
+ def arguments
37
+ return "FILE", "Documentation or code file to split."
38
+ end
39
+
40
+ # Return a short description of the program.
41
+ def description
42
+ return <<-EOF.unindent
43
+ Split the documentation of file into chunks that are printed in YAML format to
44
+ the output (to be read by codnar-weave). Many file formats can be split
45
+ depending on the specified configuration. The default configuration is called
46
+ SPLIT_HTML_DOCUMENTATION, and it preserves the whole file as a single formatted
47
+ HTML documentation chunk. This isn't very useful.
48
+
49
+ The configuration needs to specify a set of line classification patterns,
50
+ parsing states and pattern-based transitions between them, the initial state,
51
+ and expressions for formatting classified lines to HTML. See the Codnar
52
+ documentation for details.
53
+ EOF
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,367 @@
1
+ module Codnar
2
+
3
+ # A module for all the "built-in" configurations. The names of these
4
+ # configurations can be passed to the --require option of any Codnar
5
+ # Application.
6
+ module Configuration
7
+
8
+ # {{{ Documentation "splitting" configurations
9
+
10
+ # "Split" a documentation file. All lines are assumed to have the same kind
11
+ # +doc+ and no indentation is collected. Unless overriden by additional
12
+ # configuration(s), the lines are assumed to contain formatted HTML, and
13
+ # are passed as-is to the output.
14
+ #
15
+ # This is the default configuration as it performs the minimal amount of
16
+ # processing on the input. It isn't the most useful configuration.
17
+ SPLIT_HTML_DOCUMENTATION = {
18
+ "formatters" => {
19
+ "doc" => "Formatter.cast_lines(lines, 'html')",
20
+ },
21
+ "syntax" => {
22
+ "patterns" => {
23
+ "doc" => { "regexp" => "^(.*)$", "groups" => [ "payload" ] },
24
+ },
25
+ "states" => {
26
+ "start" => { "transitions" => [ { "pattern" => "doc" } ] },
27
+ },
28
+ },
29
+ }
30
+
31
+ # "Split" a documentation file containing arbitrary text, which is
32
+ # preserved by escaping it and wrapping it in an HTML pre element.
33
+ SPLIT_PRE_DOCUMENTATION = SPLIT_HTML_DOCUMENTATION.deep_merge(
34
+ "formatters" => {
35
+ "doc" => "Formatter.lines_to_pre_html(lines, :class => :doc)",
36
+ }
37
+ )
38
+
39
+ # "Split" a documentation file containing pure RDoc documentation.
40
+ SPLIT_RDOC_DOCUMENTATION = SPLIT_HTML_DOCUMENTATION.deep_merge(
41
+ "formatters" => {
42
+ "doc" => "Formatter.markup_lines_to_html(lines, 'RDoc')",
43
+ "unindented_html" => "Formatter.unindented_lines_to_html(lines)",
44
+ }
45
+ )
46
+
47
+ # "Split" a documentation file containing pure Markdown documentation.
48
+ SPLIT_MARKDOWN_DOCUMENTATION = SPLIT_HTML_DOCUMENTATION.deep_merge(
49
+ "formatters" => {
50
+ "doc" => "Formatter.markup_lines_to_html(lines, 'Markdown')",
51
+ "unindented_html" => "Formatter.unindented_lines_to_html(lines)",
52
+ }
53
+ )
54
+
55
+ # }}}
56
+
57
+ # {{{ Source code lines classification configurations
58
+
59
+ # Classify all lines as source code of some syntax (kind). This doesn't
60
+ # distinguish between comment and code lines; to do that, you need to
61
+ # combine this with comment classification configuration(s). Also, it just
62
+ # formats the lines in an HTML +pre+ element, without any syntax
63
+ # highlighting; to do that, you need to combine this with syntax
64
+ # highlighting formatting configuration(s).
65
+ CLASSIFY_SOURCE_CODE = lambda do |syntax|
66
+ return {
67
+ "formatters" => {
68
+ "#{syntax}_code" => "Formatter.lines_to_pre_html(lines, :class => :code)",
69
+ },
70
+ "syntax" => {
71
+ "patterns" => {
72
+ "#{syntax}_code" => { "regexp" => "^(\\s*)(.*)$" },
73
+ },
74
+ "states" => {
75
+ "start" => {
76
+ "transitions" => [
77
+ { "pattern" => "#{syntax}_code" },
78
+ ],
79
+ },
80
+ },
81
+ },
82
+ }
83
+ end
84
+
85
+ # }}}
86
+
87
+ # {{{ Nested foreign syntax code islands configurations
88
+
89
+ # Allow for comments containing "((( <syntax>" and "))) <syntax>" to
90
+ # designate nested islands of foreign syntax inside the normal code. The
91
+ # designator comment lines are always treated as part of the surrounding
92
+ # code, not as part of the nested foreign syntax code. There is no further
93
+ # classification of the nested foreign syntax code. Therefore, the nested
94
+ # code is not examined for begin/end chunk markers. Likewise, the nested
95
+ # code may not contain deeper nested code using a third syntax.
96
+ CLASSIFY_NESTED_CODE = lambda do |outer_syntax, inner_syntax|
97
+ {
98
+ "syntax" => {
99
+ "patterns" => {
100
+ "start_#{inner_syntax}_in_#{outer_syntax}" =>
101
+ { "regexp" => "^(\\s*)(.*\\(\\(\\(\\s*#{inner_syntax}.*)$" },
102
+ "end_#{inner_syntax}_in_#{outer_syntax}" =>
103
+ { "regexp" => "^(\\s*)(.*\\)\\)\\)\\s*#{inner_syntax}.*)$" },
104
+ "#{inner_syntax}_in_#{outer_syntax}" =>
105
+ { "regexp" => "^(\\s*)(.*)$" },
106
+ },
107
+ "states" => {
108
+ "start" => {
109
+ "transitions" => [
110
+ { "pattern" => "start_#{inner_syntax}_in_#{outer_syntax}",
111
+ "kind" => "#{outer_syntax}_code",
112
+ "next_state" => "#{inner_syntax}_in_#{outer_syntax}" },
113
+ [],
114
+ ],
115
+ },
116
+ "#{inner_syntax}_in_#{outer_syntax}" => {
117
+ "transitions" => [
118
+ { "pattern" => "end_#{inner_syntax}_in_#{outer_syntax}",
119
+ "kind" => "#{outer_syntax}_code",
120
+ "next_state" => "start" },
121
+ { "pattern" => "#{inner_syntax}_in_#{outer_syntax}",
122
+ "kind" => "#{inner_syntax}_code" },
123
+ ],
124
+ },
125
+ },
126
+ },
127
+ }
128
+ end
129
+
130
+ # }}}
131
+
132
+ # {{{ Simple comment classification configurations
133
+
134
+ # Classify simple comment lines. It accepts a restricted format: each
135
+ # comment is expected to start with some exact prefix (e.g. "#" for shell
136
+ # style comments or "//" for C++ style comments). The following space, if
137
+ # any, is stripped from the payload. As a convenience, comment that starts
138
+ # with "!" is not taken to start a comment. This both protects the 1st line
139
+ # of shell scripts ("#!"), and also any other line you wish to avoid being
140
+ # treated as a comment.
141
+ #
142
+ # This configuration is typically complemented by an additional one
143
+ # specifying how to format the (stripped!) comments; by default they are
144
+ # just displayed as-is using an HTML +pre+ element, which isn't very
145
+ # useful.
146
+ CLASSIFY_SIMPLE_COMMENTS = lambda do |prefix|
147
+ return Configuration.simple_comments(prefix)
148
+ end
149
+
150
+ # Classify simple shell ("#") comment lines.
151
+ CLASSIFY_SHELL_COMMENTS = lambda do
152
+ return Configuration.simple_comments("#")
153
+ end
154
+
155
+ # Classify simple C++ ("//") comment lines.
156
+ CLASSIFY_CPP_COMMENTS = lambda do
157
+ return Configuration.simple_comments("//")
158
+ end
159
+
160
+ # Configuration for classifying lines to comments and code based on a
161
+ # simple prefix (e.g. "#" for shell style comments or "//" for C++ style
162
+ # comments).
163
+ def self.simple_comments(prefix)
164
+ return {
165
+ "syntax" => {
166
+ "patterns" => {
167
+ "comment_#{prefix}" => { "regexp" => "^(\\s*)#{prefix}(?!!)\\s?(.*)$" },
168
+ },
169
+ "states" => {
170
+ "start" => {
171
+ "transitions" => [
172
+ { "pattern" => "comment_#{prefix}", "kind" => "comment" },
173
+ []
174
+ ],
175
+ },
176
+ },
177
+ },
178
+ }
179
+ end
180
+
181
+ # }}}
182
+
183
+ # {{{ Complex comment classification configurations
184
+
185
+ # Classify complex comment lines. It accepts a restricted format: each
186
+ # comment is expected to start with some exact prefix (e.g. "/*" for C
187
+ # style comments or "<!--" for HTML style comments). The following space,
188
+ # if any, is stripped from the payload. Following lines are also considered
189
+ # comments; a leading inner line prefix (e.g., " *" for C style comments or
190
+ # " -" for HTML style comments) with an optional following space are
191
+ # stripped from the payload. Finally, a line containing some exact suffix
192
+ # (e.g. "*/" for C style comments, or "-->" for HTML style comments) ends
193
+ # the comment. A one line comment format is also supported containing the
194
+ # prefix, the payload, and the suffix. As a convenience, comment that
195
+ # starts with "!" is not taken to start a comment. This allows protecting
196
+ # comment block you wish to avoid being classified as a comment.
197
+ #
198
+ # This configuration is typically complemented by an additional one
199
+ # specifying how to format the (stripped!) comments; by default they are
200
+ # just displayed as-is using an HTML +pre+ element, which isn't very
201
+ # useful.
202
+ CLASSIFY_COMPLEX_COMMENTS = lambda do |prefix, inner, suffix|
203
+ return Configuration.complex_comments(prefix, inner, suffix)
204
+ end
205
+
206
+ # Classify complex C ("/*", " *", " */") style comments.
207
+ CLASSIFY_C_COMMENTS = lambda do
208
+ # Since the prefix/inner/suffix passed to the configuration are regexps,
209
+ # we need to escape special characters such as "*".
210
+ return Configuration.complex_comments("/\\*", " \\*", " \\*/")
211
+ end
212
+
213
+ # Classify complex HTML ("<!--", " -", "-->") style comments.
214
+ CLASSIFY_HTML_COMMENTS = lambda do
215
+ return Configuration.complex_comments("<!--", " -", "-->")
216
+ end
217
+
218
+ # Configuration for classifying lines to comments and code based on a
219
+ # complex start prefix, inner line prefix and final suffix (e.g., "/*", "
220
+ # *", " */" for C-style comments or "<!--", " -", "-->" for HTML style
221
+ # comments).
222
+ def self.complex_comments(prefix, inner, suffix)
223
+ return {
224
+ "syntax" => {
225
+ "patterns" => {
226
+ "comment_prefix_#{prefix}" => { "regexp" => "^(\\s*)#{prefix}(?!!)\\s?(.*)$" },
227
+ "comment_inner_#{inner}" => { "regexp" => "^(\\s*)#{inner}\\s?(.*)$" },
228
+ "comment_suffix_#{suffix}" => { "regexp" => "^(\\s*)#{suffix}\\s*$" },
229
+ "comment_line_#{prefix}_#{suffix}" => { "regexp" => "^(\\s*)#{prefix}(?!!)\s?(.*?)\s*#{suffix}\\s*$" },
230
+ },
231
+ "states" => {
232
+ "start" => {
233
+ "transitions" => [
234
+ { "pattern" => "comment_line_#{prefix}_#{suffix}",
235
+ "kind" => "comment" },
236
+ { "pattern" => "comment_prefix_#{prefix}",
237
+ "kind" => "comment",
238
+ "next_state" => "comment_#{prefix}" },
239
+ [],
240
+ ],
241
+ },
242
+ "comment_#{prefix}" => {
243
+ "transitions" => [
244
+ { "pattern" => "comment_suffix_#{suffix}",
245
+ "kind" => "comment",
246
+ "next_state" => "start" },
247
+ { "pattern" => "comment_inner_#{inner}",
248
+ "kind" => "comment" },
249
+ ],
250
+ },
251
+ },
252
+ },
253
+ }
254
+ end
255
+
256
+ # }}}
257
+
258
+ # {{{ Comment formatting configurations
259
+
260
+ # Format comments as HTML pre elements. Is used to complement a
261
+ # configuration that classifies some lines as +comment+.
262
+ FORMAT_PRE_COMMENTS = {
263
+ "formatters" => {
264
+ "comment" => "Formatter.lines_to_pre_html(lines, :class => :comment)",
265
+ },
266
+ }
267
+
268
+ # Format comments that use the RDoc notation. Is used to complement a
269
+ # configuration that classifies some lines as +comment+.
270
+ FORMAT_RDOC_COMMENTS = {
271
+ "formatters" => {
272
+ "comment" => "Formatter.markup_lines_to_html(lines, 'RDoc')",
273
+ "unindented_html" => "Formatter.unindented_lines_to_html(lines)",
274
+ },
275
+ }
276
+
277
+ # Format comments that use the Markdown notation. Is used to complement a
278
+ # configuration that classifies some lines as +comment+.
279
+ FORMAT_MARKDOWN_COMMENTS = {
280
+ "formatters" => {
281
+ "comment" => "Formatter.markup_lines_to_html(lines, 'Markdown')",
282
+ "unindented_html" => "Formatter.unindented_lines_to_html(lines)",
283
+ },
284
+ }
285
+
286
+ # }}}
287
+
288
+ # {{{ GVim syntax highlighting formatting configurations
289
+
290
+ # Format code using GVim's Ruby syntax highlighting, using explicit HTML
291
+ # constructs. Assumes some previous configuration already classified the
292
+ # code lines.
293
+ FORMAT_CODE_GVIM_HTML = lambda do |syntax|
294
+ return Configuration.gvim_code_format(syntax)
295
+ end
296
+
297
+ # Format code using GVim's Ruby syntax highlighting, using CSS classes
298
+ # instead of explicit font and color styles. Assumes some previous
299
+ # configuration already classified the code lines.
300
+ FORMAT_CODE_GVIM_CSS = lambda do |syntax|
301
+ return Configuration.gvim_code_format(syntax, "'+:let html_use_css=1'")
302
+ end
303
+
304
+ # Return a configuration for highlighting a specific syntax using GVim.
305
+ def self.gvim_code_format(syntax, extra_commands = "")
306
+ return {
307
+ "formatters" => {
308
+ "#{syntax}_code" => "GVim.lines_to_html(lines, '#{syntax}', [ #{extra_commands} ])",
309
+ },
310
+ }
311
+ end
312
+
313
+ # }}}
314
+
315
+ # {{{ Sunlight syntax highlighting formatting configurations
316
+
317
+ # Format code using Sunlight's syntax highlighting. This assumes the HTML
318
+ # will include and invoke Sunlight's Javascript file which does the
319
+ # highlighting on the fly inside the DOM, instead of pre-computing it when
320
+ # splitting the file.
321
+ FORMAT_CODE_SUNLIGHT = lambda do |syntax|
322
+ return Configuration.sunlight_code_format(syntax)
323
+ end
324
+
325
+ # Return a configuration for highlighting a specific syntax using Sunlight.
326
+ def self.sunlight_code_format(syntax)
327
+ return {
328
+ "formatters" => {
329
+ "#{syntax}_code" => "Sunlight.lines_to_html(lines, '#{syntax}')",
330
+ },
331
+ }
332
+ end
333
+
334
+ # }}}
335
+
336
+ # {{{ Chunk splitting configurations
337
+
338
+ # Group lines into chunks using VIM-style "{{{"/"}}}" region designations.
339
+ # Assumes other configurations handle the actual content lines.
340
+ CHUNK_BY_VIM_REGIONS = {
341
+ "formatters" => {
342
+ "begin_chunk" => "[]",
343
+ "end_chunk" => "[]",
344
+ "nested_chunk" => "Formatter.nested_chunk_lines_to_html(lines)",
345
+ },
346
+ "syntax" => {
347
+ "patterns" => {
348
+ "begin_chunk" => { "regexp" => "^(\\s*)\\W*\\{\\{\\{\\s*(.*?)\\s*$" },
349
+ "end_chunk" => { "regexp" => "^(\\s*)\\W*\\}\\}\\}\\s*(.*?)\\s*$" },
350
+ },
351
+ "states" => {
352
+ "start" => {
353
+ "transitions" => [
354
+ { "pattern" => "begin_chunk" },
355
+ { "pattern" => "end_chunk" },
356
+ [],
357
+ ],
358
+ },
359
+ },
360
+ },
361
+ }
362
+
363
+ # }}}
364
+
365
+ end
366
+
367
+ end
@@ -0,0 +1,32 @@
1
+ module Codnar
2
+
3
+ # Split disk files into chunks.
4
+ class Splitter
5
+
6
+ # Construct a splitter based on a configuration in the following structure:
7
+ #
8
+ # syntax: <syntax>
9
+ # formatters:
10
+ # <kind>: <expression>
11
+ #
12
+ # Where the syntax is passed as-is to (and expanded in-place by) a Scanner,
13
+ # and the formatters are passed as-is to a Formatter to convert the chunk's
14
+ # classified lines into HTML.
15
+ def initialize(errors, configuration)
16
+ @errors = errors
17
+ @configuration = configuration
18
+ @scanner = Scanner.new(errors, configuration.syntax)
19
+ @formatter = Formatter.new(errors, configuration.formatters)
20
+ end
21
+
22
+ # Split a disk file into HTML chunks.
23
+ def chunks(path)
24
+ lines = @scanner.lines(path)
25
+ chunks = Merger.chunks(@errors, path, lines)
26
+ chunks.each { |chunk| chunk.html = @formatter.lines_to_html(chunk.delete("lines")) }
27
+ return chunks
28
+ end
29
+
30
+ end
31
+
32
+ end