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.
- data/ChangeLog +165 -0
- data/LICENSE +19 -0
- data/README.rdoc +32 -0
- data/Rakefile +66 -0
- data/bin/codnar-split +5 -0
- data/bin/codnar-weave +5 -0
- data/codnar.html +10945 -0
- data/doc/logo.png +0 -0
- data/doc/root.html +22 -0
- data/doc/story.markdown +180 -0
- data/doc/system.markdown +671 -0
- data/lib/codnar.rb +41 -0
- data/lib/codnar/application.rb +92 -0
- data/lib/codnar/cache.rb +61 -0
- data/lib/codnar/data/contents.js +113 -0
- data/lib/codnar/data/control_chunks.js +44 -0
- data/lib/codnar/data/style.css +95 -0
- data/lib/codnar/data/sunlight/README.txt +4 -0
- data/lib/codnar/data/sunlight/css-min.js +1 -0
- data/lib/codnar/data/sunlight/default.css +236 -0
- data/lib/codnar/data/sunlight/javascript-min.js +1 -0
- data/lib/codnar/data/sunlight/min.js +1 -0
- data/lib/codnar/data/sunlight/ruby-min.js +1 -0
- data/lib/codnar/data/yui/README.txt +3 -0
- data/lib/codnar/data/yui/base.css +132 -0
- data/lib/codnar/data/yui/reset.css +142 -0
- data/lib/codnar/formatter.rb +180 -0
- data/lib/codnar/grouper.rb +28 -0
- data/lib/codnar/gvim.rb +132 -0
- data/lib/codnar/hash_extensions.rb +41 -0
- data/lib/codnar/markdown.rb +47 -0
- data/lib/codnar/merger.rb +138 -0
- data/lib/codnar/rake.rb +41 -0
- data/lib/codnar/rake/split_task.rb +71 -0
- data/lib/codnar/rake/weave_task.rb +59 -0
- data/lib/codnar/rdoc.rb +9 -0
- data/lib/codnar/reader.rb +121 -0
- data/lib/codnar/scanner.rb +216 -0
- data/lib/codnar/split.rb +58 -0
- data/lib/codnar/split_configurations.rb +367 -0
- data/lib/codnar/splitter.rb +32 -0
- data/lib/codnar/string_extensions.rb +25 -0
- data/lib/codnar/sunlight.rb +17 -0
- data/lib/codnar/version.rb +8 -0
- data/lib/codnar/weave.rb +58 -0
- data/lib/codnar/weave_configurations.rb +48 -0
- data/lib/codnar/weaver.rb +105 -0
- data/lib/codnar/writer.rb +38 -0
- data/test/cache_computations.rb +41 -0
- data/test/deep_merge.rb +29 -0
- data/test/embed_images.rb +12 -0
- data/test/expand_markdown.rb +27 -0
- data/test/expand_rdoc.rb +20 -0
- data/test/format_code_gvim_configurations.rb +55 -0
- data/test/format_code_sunlight_configurations.rb +37 -0
- data/test/format_comment_configurations.rb +86 -0
- data/test/format_lines.rb +72 -0
- data/test/group_lines.rb +31 -0
- data/test/gvim_highlight_syntax.rb +49 -0
- data/test/identify_chunks.rb +32 -0
- data/test/lib/test_with_configurations.rb +15 -0
- data/test/merge_lines.rb +133 -0
- data/test/rake_tasks.rb +38 -0
- data/test/read_chunks.rb +110 -0
- data/test/run_application.rb +56 -0
- data/test/run_split.rb +38 -0
- data/test/run_weave.rb +75 -0
- data/test/scan_lines.rb +78 -0
- data/test/split_chunk_configurations.rb +55 -0
- data/test/split_code.rb +109 -0
- data/test/split_code_configurations.rb +73 -0
- data/test/split_combined_configurations.rb +114 -0
- data/test/split_complex_comment_configurations.rb +73 -0
- data/test/split_documentation.rb +92 -0
- data/test/split_documentation_configurations.rb +97 -0
- data/test/split_simple_comment_configurations.rb +50 -0
- data/test/sunlight_highlight_syntax.rb +25 -0
- data/test/weave_configurations.rb +144 -0
- data/test/write_chunks.rb +28 -0
- metadata +363 -0
data/lib/codnar.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require "andand"
|
2
|
+
require "base64"
|
3
|
+
require "cgi"
|
4
|
+
require "digest/sha2"
|
5
|
+
require "erb"
|
6
|
+
require "fileutils"
|
7
|
+
require "irb"
|
8
|
+
require "rdiscount"
|
9
|
+
require "rdoc/markup/to_html"
|
10
|
+
require "tempfile"
|
11
|
+
require "yaml"
|
12
|
+
|
13
|
+
require "olag/application"
|
14
|
+
require "olag/data_files"
|
15
|
+
require "olag/errors"
|
16
|
+
require "olag/hash_struct"
|
17
|
+
require "olag/string_unindent"
|
18
|
+
|
19
|
+
require "codnar/version"
|
20
|
+
|
21
|
+
require "codnar/hash_extensions"
|
22
|
+
require "codnar/markdown"
|
23
|
+
require "codnar/rdoc"
|
24
|
+
require "codnar/string_extensions"
|
25
|
+
|
26
|
+
require "codnar/application"
|
27
|
+
require "codnar/cache"
|
28
|
+
require "codnar/formatter"
|
29
|
+
require "codnar/grouper"
|
30
|
+
require "codnar/gvim"
|
31
|
+
require "codnar/merger"
|
32
|
+
require "codnar/split"
|
33
|
+
require "codnar/reader"
|
34
|
+
require "codnar/scanner"
|
35
|
+
require "codnar/split_configurations"
|
36
|
+
require "codnar/splitter"
|
37
|
+
require "codnar/sunlight"
|
38
|
+
require "codnar/weave"
|
39
|
+
require "codnar/weave_configurations"
|
40
|
+
require "codnar/weaver"
|
41
|
+
require "codnar/writer"
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Codnar
|
2
|
+
|
3
|
+
# Base class for Codnar applications.
|
4
|
+
class Application < Olag::Application
|
5
|
+
|
6
|
+
# Create a Codnar application.
|
7
|
+
def initialize(is_test = nil)
|
8
|
+
super(is_test)
|
9
|
+
@configuration ||= {}
|
10
|
+
end
|
11
|
+
|
12
|
+
# Run the Codnar application, returning its status.
|
13
|
+
def run(&block)
|
14
|
+
super(@configuration, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
# Define Codnar application flags.
|
20
|
+
def define_flags
|
21
|
+
super
|
22
|
+
define_include_flag
|
23
|
+
define_require_flag
|
24
|
+
define_merge_flag
|
25
|
+
define_print_flag
|
26
|
+
end
|
27
|
+
|
28
|
+
# Return the application's version - that is, Codnar's version.
|
29
|
+
def version
|
30
|
+
return Codnar::VERSION
|
31
|
+
end
|
32
|
+
|
33
|
+
# Define a flag for collecting module load path directories.
|
34
|
+
def define_include_flag
|
35
|
+
@options.on("-I", "--include DIRECTORY", String, "Add directory to Ruby's load path.") do |path|
|
36
|
+
$LOAD_PATH.unshift(path)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Define a flag for loading a Ruby module. This may be needed for
|
41
|
+
# user-specified configurations to work.
|
42
|
+
def define_require_flag
|
43
|
+
@options.on("-r", "--require MODULE", String, "Load a Ruby module for user configurations.") do |path|
|
44
|
+
begin
|
45
|
+
require(path)
|
46
|
+
rescue Exception => exception
|
47
|
+
$stderr.puts("#{$0}: #{exception}")
|
48
|
+
exit(1)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Define a flag for applying (merging) a Codnar configuration.
|
54
|
+
def define_merge_flag
|
55
|
+
@options.on("-c", "--configuration NAME-or-FILE", String, "Apply a named or disk file configuration.") do |name_or_path|
|
56
|
+
loaded_configuration = load_configuration(name_or_path)
|
57
|
+
@configuration = @configuration.deep_merge(loaded_configuration)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Define a flag for printing the (merged) Codnar configuration.
|
62
|
+
def define_print_flag
|
63
|
+
@options.on("-p", "--print", "Print the merged configuration.") do |name_or_path|
|
64
|
+
puts(@configuration.to_yaml)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Load a configuration either from the available builtin data or from a
|
69
|
+
# disk file.
|
70
|
+
def load_configuration(name_or_path)
|
71
|
+
return YAML.load_file(name_or_path) if File.exist?(name_or_path)
|
72
|
+
name, *arguments = name_or_path.split(':')
|
73
|
+
value = configuration_value(name)
|
74
|
+
value = value.call(*arguments) unless Hash === value
|
75
|
+
return value
|
76
|
+
end
|
77
|
+
|
78
|
+
# Compute the value of a named built-in configuration.
|
79
|
+
def configuration_value(name)
|
80
|
+
begin
|
81
|
+
value = Configuration.const_get(name.upcase)
|
82
|
+
return value if value
|
83
|
+
rescue
|
84
|
+
value = nil
|
85
|
+
end
|
86
|
+
$stderr.puts("#{$0}: Configuration: #{name} is neither a disk file nor a known configuration")
|
87
|
+
exit(1)
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
data/lib/codnar/cache.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
module Codnar
|
2
|
+
|
3
|
+
# Cache long computations in disk files.
|
4
|
+
class Cache
|
5
|
+
|
6
|
+
# Whether to recompute values even if they are cached.
|
7
|
+
attr_accessor :force_recompute
|
8
|
+
|
9
|
+
# Connect to an existing disk cache. The cache is expected to be stored in
|
10
|
+
# a directory of the specified name, which is either in the current working
|
11
|
+
# directory or in one of its parent directories.
|
12
|
+
def initialize(directory, &block)
|
13
|
+
@force_recompute = false
|
14
|
+
@computation = block
|
15
|
+
@directory = find_directory(Dir.pwd, directory)
|
16
|
+
if @directory
|
17
|
+
class <<self; alias [] :cached_computation; end
|
18
|
+
else
|
19
|
+
class <<self; alias [] :uncached_computation; end
|
20
|
+
$stderr.puts("#{$0}: Could not find cache directory: #{directory}.")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Access the results of the computation for the specified input. Fetch the
|
25
|
+
# result from the cache if it is there, otherwise invoke the computation
|
26
|
+
# and store the result in the cache for the next time.
|
27
|
+
def cached_computation(input)
|
28
|
+
file = cache_file(input)
|
29
|
+
return YAML.load_file(file) if File.exists?(file) and not @force_recompute
|
30
|
+
result = @computation.call(input)
|
31
|
+
File.open(file, "w") { |file| file.write(result.to_yaml) }
|
32
|
+
return result
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return the file expected to cache the computed results for a given input,
|
36
|
+
def cache_file(input)
|
37
|
+
key = Digest.hexencode(Digest::SHA2.digest(input.to_yaml))
|
38
|
+
return @directory + "/" + key + ".yaml"
|
39
|
+
end
|
40
|
+
|
41
|
+
# Access the results of a computation for the specified input, in case we
|
42
|
+
# do not have a cache directory to look for and store the results in.
|
43
|
+
def uncached_computation(input)
|
44
|
+
return @computation.call(input)
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
# Find the path of the cache directory, search from the given working
|
50
|
+
# directory upward until finding a match.
|
51
|
+
def find_directory(working_directory, cache_directory)
|
52
|
+
directory = working_directory + "/" + cache_directory
|
53
|
+
return directory if File.exists?(directory)
|
54
|
+
parent_directory = File.dirname(working_directory)
|
55
|
+
return nil if parent_directory == working_directory
|
56
|
+
return find_directory(parent_directory, cache_directory)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
/*
|
2
|
+
* Quick-and-dirty JS for inserting a table of content inside a DIV with the id
|
3
|
+
* "contents". The table of content is a series of nested UL and LI elements,
|
4
|
+
* prefixed with an H1 containing the text "0 Contents". This H1 comes in
|
5
|
+
* addition to the single static H1 expected by HTML best practices. It looks
|
6
|
+
* "right" and should not confuse search engines etc. since they do not execute
|
7
|
+
* Javascript code.
|
8
|
+
*/
|
9
|
+
function inject_contents() {
|
10
|
+
var contents = document.getElementById("contents");
|
11
|
+
var lists = contents_lists();
|
12
|
+
contents.appendChild(contents_header()); // TRICKY: Must be done after contents_lists().
|
13
|
+
contents.appendChild(lists);
|
14
|
+
}
|
15
|
+
|
16
|
+
/*
|
17
|
+
* Create a table of contents H1.
|
18
|
+
*/
|
19
|
+
function contents_header() {
|
20
|
+
var h = document.createElement("h1");
|
21
|
+
var text = document.createTextNode("Contents");
|
22
|
+
h.appendChild(text);
|
23
|
+
return h;
|
24
|
+
}
|
25
|
+
|
26
|
+
/*
|
27
|
+
* Create nested UL/LI lists for the table of content.
|
28
|
+
*/
|
29
|
+
function contents_lists() {
|
30
|
+
var container;
|
31
|
+
var indices = [];
|
32
|
+
var h_elements = all_h_elements();
|
33
|
+
for (var e in h_elements) {
|
34
|
+
h = h_elements[e];
|
35
|
+
var level = h.tagName.substring(1, 2) - 1;
|
36
|
+
container = pop_container(container, indices, level);
|
37
|
+
container = push_container(container, indices, level);
|
38
|
+
var id = indices.join(".");
|
39
|
+
container.appendChild(list_element(id, h));
|
40
|
+
h.insertBefore(header_anchor(id), h.firstChild);
|
41
|
+
}
|
42
|
+
return pop_container(container, indices, 1);
|
43
|
+
}
|
44
|
+
|
45
|
+
/*
|
46
|
+
* Get a list of all H elements in the DOM. We skip the single H1 element;
|
47
|
+
* otherwise it would just have the index "1" which would be prefixed to all
|
48
|
+
* other headers.
|
49
|
+
*/
|
50
|
+
function all_h_elements() {
|
51
|
+
var elements = document.getElementsByTagName("*");
|
52
|
+
var h_elements = [];
|
53
|
+
for (var e in elements) {
|
54
|
+
var h = elements[e];
|
55
|
+
if (/^h[2-9]$/i.test(h.tagName)) h_elements.push(h);
|
56
|
+
}
|
57
|
+
return h_elements;
|
58
|
+
}
|
59
|
+
|
60
|
+
/*
|
61
|
+
* Pop indices (and UL containers) until reaching up to a given level.
|
62
|
+
*/
|
63
|
+
function pop_container(container, indices, level) {
|
64
|
+
while (indices.length > level) {
|
65
|
+
container = container.parentNode;
|
66
|
+
indices.pop();
|
67
|
+
}
|
68
|
+
return container;
|
69
|
+
}
|
70
|
+
|
71
|
+
/*
|
72
|
+
* Push indices (and UL containers) until reaching doen to a given level.
|
73
|
+
*/
|
74
|
+
function push_container(container, indices, level) {
|
75
|
+
while (indices.length < level) {
|
76
|
+
// TRICKY: push a 0 for the very last new level, so the ++ at the end
|
77
|
+
// will turn it into a 1.
|
78
|
+
indices.push(indices.level < level - 1);
|
79
|
+
var ul = document.createElement("ul");
|
80
|
+
if (container) {
|
81
|
+
container.appendChild(ul);
|
82
|
+
}
|
83
|
+
container = ul;
|
84
|
+
}
|
85
|
+
indices[indices.length - 1]++;
|
86
|
+
return container;
|
87
|
+
}
|
88
|
+
|
89
|
+
/*
|
90
|
+
* Create a LI for an H element with some id.
|
91
|
+
*/
|
92
|
+
function list_element(id, h) {
|
93
|
+
var a = document.createElement("a");
|
94
|
+
a.href = "#" + id;
|
95
|
+
a.innerHTML = id + " " + h.innerHTML;
|
96
|
+
var li = document.createElement("li");
|
97
|
+
li.appendChild(a);
|
98
|
+
return li;
|
99
|
+
}
|
100
|
+
|
101
|
+
/*
|
102
|
+
* Create an anchor for an H element with some id.
|
103
|
+
*/
|
104
|
+
function header_anchor(id) {
|
105
|
+
var text = document.createTextNode(id + " ");
|
106
|
+
var a = document.createElement("a");
|
107
|
+
a.id = id;
|
108
|
+
a.appendChild(text);
|
109
|
+
return a;
|
110
|
+
}
|
111
|
+
|
112
|
+
/* Only invoke it after all helper functions are defined. */
|
113
|
+
inject_contents();
|
@@ -0,0 +1,44 @@
|
|
1
|
+
/*
|
2
|
+
* Quick-and-dirty JS for inserting a "+"/"-" control for chunk visibility next
|
3
|
+
* to each chunk's name. By default, all chunks are hidden.
|
4
|
+
*/
|
5
|
+
function inject_chunk_controls() {
|
6
|
+
var name_div;
|
7
|
+
foreach_chunk_elements(function(div) {
|
8
|
+
name_div = div;
|
9
|
+
}, function(html_div) {
|
10
|
+
var control_span = document.createElement("span");
|
11
|
+
var hide = function() {
|
12
|
+
control_span.innerHTML = "+";
|
13
|
+
html_div.style.display = "none";
|
14
|
+
}
|
15
|
+
var show = function() {
|
16
|
+
control_span.innerHTML = "–"; // Vertical bar.
|
17
|
+
html_div.style.display = "block";
|
18
|
+
}
|
19
|
+
name_div.onclick = function() {
|
20
|
+
html_div.style.display == "block" ? hide() : show();
|
21
|
+
}
|
22
|
+
hide(); // Initializes html_div.style.display
|
23
|
+
control_span.className = "control chunk";
|
24
|
+
name_div.insertBefore(control_span, name_div.firstChild);
|
25
|
+
})
|
26
|
+
}
|
27
|
+
|
28
|
+
/*
|
29
|
+
* Loop on all DIV elements that contain a chunk name, or that contain chunk
|
30
|
+
* HTML. Assumes that they come in pairs - name first, HTML second.
|
31
|
+
*/
|
32
|
+
function foreach_chunk_elements(name_lambda, html_lambda) {
|
33
|
+
var div_elements = document.getElementsByTagName("div");
|
34
|
+
for (var e in div_elements) {
|
35
|
+
var div = div_elements[e];
|
36
|
+
classes = " " + div.className + " ";
|
37
|
+
if (!/ chunk /.test(classes)) continue;
|
38
|
+
if (/ name /.test(classes)) name_lambda(div);
|
39
|
+
if (/ html /.test(classes)) html_lambda(div);
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
/* Only invoke it after all helper functions are defined. */
|
44
|
+
inject_chunk_controls();
|
@@ -0,0 +1,95 @@
|
|
1
|
+
/* Margin & Padding */
|
2
|
+
|
3
|
+
div.chunk.name,
|
4
|
+
div.chunk.html,
|
5
|
+
div.chunk.containers,
|
6
|
+
div.chunk table,
|
7
|
+
div.chunk td,
|
8
|
+
div.chunk pre {
|
9
|
+
margin: 0;
|
10
|
+
padding: 0;
|
11
|
+
}
|
12
|
+
div.chunk *:last-child {
|
13
|
+
margin-bottom: 0;
|
14
|
+
}
|
15
|
+
h4, h5, h6,
|
16
|
+
div.chunk,
|
17
|
+
div.comment pre {
|
18
|
+
margin: 1em 0;
|
19
|
+
}
|
20
|
+
pre,
|
21
|
+
div.comment,
|
22
|
+
div.chunk.html {
|
23
|
+
padding: 0.33em;
|
24
|
+
}
|
25
|
+
|
26
|
+
span.control.chunk {
|
27
|
+
padding-left: 0.25em;
|
28
|
+
padding-right: 0.25em;
|
29
|
+
}
|
30
|
+
|
31
|
+
/* Table of content */
|
32
|
+
|
33
|
+
div#contents ul {
|
34
|
+
margin-top: 0;
|
35
|
+
margin-bottom: 0;
|
36
|
+
padding: 0;
|
37
|
+
}
|
38
|
+
|
39
|
+
div#contents li {
|
40
|
+
list-style-type: none;
|
41
|
+
}
|
42
|
+
|
43
|
+
/* Lists */
|
44
|
+
|
45
|
+
ul.chunk.containers {
|
46
|
+
padding: 0;
|
47
|
+
margin: 0;
|
48
|
+
display: inline;
|
49
|
+
}
|
50
|
+
ul.chunk.containers li {
|
51
|
+
display: inline;
|
52
|
+
list-style-type: none;
|
53
|
+
}
|
54
|
+
|
55
|
+
/* Borders */
|
56
|
+
|
57
|
+
pre,
|
58
|
+
span.control.chunk,
|
59
|
+
div.chunk.html {
|
60
|
+
border: 1px solid #000;
|
61
|
+
}
|
62
|
+
|
63
|
+
table.layout td.indentation,
|
64
|
+
div.chunk pre {
|
65
|
+
border: none;
|
66
|
+
}
|
67
|
+
|
68
|
+
/* Colors */
|
69
|
+
|
70
|
+
span.control.chunk,
|
71
|
+
table.layout td.html {
|
72
|
+
background-color: Beige;
|
73
|
+
}
|
74
|
+
|
75
|
+
/* Colors for GVim classes */
|
76
|
+
|
77
|
+
span.Constant { color: Crimson; }
|
78
|
+
span.Identifier { color: Teal; }
|
79
|
+
span.PreProc { color: Indigo; }
|
80
|
+
span.Special { color: Navy; }
|
81
|
+
span.Statement { color: Maroon; }
|
82
|
+
span.Type { color: Green; }
|
83
|
+
span.Comment { color: Purple; }
|
84
|
+
|
85
|
+
/* Fonts */
|
86
|
+
|
87
|
+
body {
|
88
|
+
font-family: Sans-Serif;
|
89
|
+
}
|
90
|
+
pre {
|
91
|
+
font-family: Consolas, Inconsolata, Monaco, "Courier New", Monospace;
|
92
|
+
}
|
93
|
+
div.chunk.name {
|
94
|
+
font-weight: bold;
|
95
|
+
}
|