codnar 0.1.64

Sign up to get free protection for your applications and to get access to all the features.
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