glyph 0.3.0 → 0.4.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 (162) hide show
  1. data/.gitignore +7 -0
  2. data/AUTHORS.textile +8 -7
  3. data/CHANGELOG.textile +89 -8
  4. data/LICENSE.textile +1 -2
  5. data/README.textile +89 -61
  6. data/Rakefile +12 -10
  7. data/VERSION +1 -1
  8. data/benchmark.rb +1 -1
  9. data/book/config.yml +18 -4
  10. data/book/document.glyph +269 -45
  11. data/book/images/glyph/commands_tasks.png +0 -0
  12. data/book/images/{document_generation.png → glyph/document_generation.png} +0 -0
  13. data/book/images/glyph/glyph.eps +123 -0
  14. data/book/images/glyph/glyph.png +0 -0
  15. data/book/images/glyph/glyph.svg +29 -0
  16. data/book/lib/commands/commands.rb +11 -0
  17. data/book/lib/layouts/bookindex.glyph +127 -0
  18. data/book/lib/layouts/bookpage.glyph +129 -0
  19. data/book/lib/layouts/project.glyph +26 -0
  20. data/book/lib/macros/reference.rb +27 -7
  21. data/book/lib/tasks/tasks.rake +52 -0
  22. data/book/snippets.yml +1 -1
  23. data/book/text/{acknowledgement.glyph → acknowledgements.glyph} +4 -2
  24. data/book/text/changelog.glyph +29 -3
  25. data/book/text/compiling/compiling.glyph +44 -20
  26. data/book/text/compiling/lite_mode.glyph +0 -4
  27. data/book/text/compiling/programmatic_usage.glyph +1 -5
  28. data/book/text/config/document.glyph +35 -0
  29. data/book/text/config/filters.glyph +28 -0
  30. data/book/text/config/options.glyph +25 -0
  31. data/book/text/config/output.glyph +83 -0
  32. data/book/text/extending/bookmarks_headers.glyph +0 -5
  33. data/book/text/extending/command.glyph +56 -0
  34. data/book/text/extending/commands_tasks.glyph +39 -0
  35. data/book/text/extending/further_reading.glyph +0 -3
  36. data/book/text/extending/internals.glyph +3 -5
  37. data/book/text/extending/interpreting.glyph +0 -4
  38. data/book/text/extending/layouts.glyph +68 -0
  39. data/book/text/extending/macro_def.glyph +0 -5
  40. data/book/text/extending/output_format.glyph +78 -0
  41. data/book/text/extending/params_attrs.glyph +0 -3
  42. data/book/text/extending/placeholders.glyph +0 -4
  43. data/book/text/extending/task.glyph +46 -0
  44. data/book/text/extending/validators.glyph +5 -6
  45. data/book/text/getting_started/configuration.glyph +1 -5
  46. data/book/text/getting_started/create_project.glyph +1 -5
  47. data/book/text/getting_started/structure.glyph +0 -4
  48. data/book/text/introduction.glyph +100 -75
  49. data/book/text/license.glyph +1 -2
  50. data/book/text/macros/macros_block.glyph +8 -4
  51. data/book/text/macros/macros_core.glyph +0 -3
  52. data/book/text/macros/macros_filters.glyph +2 -7
  53. data/book/text/macros/macros_inline.glyph +0 -4
  54. data/book/text/macros/macros_structure.glyph +0 -4
  55. data/book/text/ref_commands.glyph +29 -7
  56. data/book/text/stats/bookmarks.glyph +49 -0
  57. data/book/text/stats/links.glyph +90 -0
  58. data/book/text/stats/macros.glyph +73 -0
  59. data/book/text/stats/snippets.glyph +50 -0
  60. data/book/text/stats/stats.glyph +79 -0
  61. data/book/text/text_editing/attribute_intro.glyph +22 -0
  62. data/book/text/text_editing/code.glyph +0 -5
  63. data/book/text/text_editing/conditionals.glyph +0 -4
  64. data/book/text/text_editing/esc_quot.glyph +64 -0
  65. data/book/text/text_editing/evaluation.glyph +0 -3
  66. data/book/text/text_editing/glyph_files.glyph +0 -3
  67. data/book/text/text_editing/images.glyph +0 -5
  68. data/book/text/text_editing/inclusions.glyph +0 -4
  69. data/book/text/text_editing/links.glyph +2 -7
  70. data/book/text/text_editing/macro_intro.glyph +1 -98
  71. data/book/text/text_editing/raw_html.glyph +0 -87
  72. data/book/text/text_editing/section_aliases.glyph +28 -0
  73. data/book/text/text_editing/sections.glyph +1 -32
  74. data/book/text/text_editing/stylesheets.glyph +3 -5
  75. data/book/text/text_editing/topics.glyph +33 -0
  76. data/book/text/text_editing/xml_fallback.glyph +73 -0
  77. data/book/text/troubleshooting/errors_command.glyph +0 -3
  78. data/book/text/troubleshooting/errors_generic.glyph +21 -6
  79. data/book/text/troubleshooting/errors_macro.glyph +11 -8
  80. data/book/text/troubleshooting/errors_parser.glyph +0 -3
  81. data/config.yml +60 -25
  82. data/glyph.gemspec +90 -36
  83. data/layouts/web/index.glyph +16 -0
  84. data/layouts/web/topic.glyph +15 -0
  85. data/layouts/web5/index.glyph +16 -0
  86. data/layouts/web5/topic.glyph +17 -0
  87. data/lib/glyph.rb +36 -49
  88. data/lib/glyph/analyzer.rb +253 -0
  89. data/lib/glyph/bookmark.rb +92 -0
  90. data/lib/glyph/commands.rb +9 -221
  91. data/lib/glyph/commands/add.rb +8 -0
  92. data/lib/glyph/commands/compile.rb +93 -0
  93. data/lib/glyph/commands/config.rb +38 -0
  94. data/lib/glyph/commands/init.rb +6 -0
  95. data/lib/glyph/commands/outline.rb +45 -0
  96. data/lib/glyph/commands/stats.rb +48 -0
  97. data/lib/glyph/commands/todo.rb +29 -0
  98. data/lib/glyph/config.rb +2 -0
  99. data/lib/glyph/document.rb +61 -30
  100. data/lib/glyph/interpreter.rb +2 -2
  101. data/lib/glyph/macro.rb +14 -5
  102. data/lib/glyph/macro_helpers.rb +280 -0
  103. data/lib/glyph/macro_validators.rb +37 -2
  104. data/lib/glyph/reporter.rb +182 -0
  105. data/lib/glyph/syntax_node.rb +37 -10
  106. data/lib/glyph/system_extensions.rb +8 -45
  107. data/lib/glyph/utils.rb +148 -0
  108. data/macros/core.rb +10 -15
  109. data/macros/filters.rb +4 -5
  110. data/macros/html/block.rb +46 -30
  111. data/macros/html/inline.rb +9 -35
  112. data/macros/html/structure.rb +59 -72
  113. data/macros/html5/block.rb +69 -0
  114. data/macros/html5/inline.rb +24 -0
  115. data/macros/html5/structure.rb +139 -0
  116. data/macros/xml.rb +1 -1
  117. data/spec/files/custom_command.rb +6 -0
  118. data/spec/files/custom_tasks.rake +6 -0
  119. data/spec/files/document_for_stats.glyph +12 -0
  120. data/spec/files/references.glyph +4 -0
  121. data/spec/files/web1.glyph +11 -0
  122. data/spec/files/web2.glyph +10 -0
  123. data/spec/files/web_doc.glyph +23 -0
  124. data/spec/lib/analyzer_spec.rb +137 -0
  125. data/spec/lib/bookmark_spec.rb +64 -0
  126. data/spec/lib/commands_spec.rb +30 -5
  127. data/spec/lib/document_spec.rb +49 -9
  128. data/spec/lib/glyph_spec.rb +21 -1
  129. data/spec/lib/macro_spec.rb +6 -6
  130. data/spec/lib/macro_validators_spec.rb +24 -0
  131. data/spec/lib/reporter_spec.rb +132 -0
  132. data/spec/macros/core_spec.rb +2 -3
  133. data/spec/macros/filters_spec.rb +2 -2
  134. data/spec/macros/html5_spec.rb +101 -0
  135. data/spec/macros/macros_spec.rb +16 -6
  136. data/spec/macros/web5_spec.rb +32 -0
  137. data/spec/macros/web_spec.rb +59 -0
  138. data/spec/macros/xml_spec.rb +1 -1
  139. data/spec/spec_helper.rb +24 -4
  140. data/spec/tasks/generate_spec.rb +54 -0
  141. data/spec/tasks/load_spec.rb +29 -3
  142. data/spec/tasks/project_spec.rb +21 -3
  143. data/styles/default.css +40 -4
  144. data/styles/pagination.css +59 -41
  145. data/tasks/generate.rake +110 -31
  146. data/tasks/load.rake +39 -7
  147. data/tasks/project.rake +9 -7
  148. metadata +115 -34
  149. data/book/images/glyph.png +0 -0
  150. data/book/images/glyph.svg +0 -351
  151. data/book/output/html/glyph.html +0 -4482
  152. data/book/output/html/images/document_generation.png +0 -0
  153. data/book/output/html/images/glyph.png +0 -0
  154. data/book/output/html/images/glyph.svg +0 -351
  155. data/book/output/pdf/glyph.pdf +4 -10254
  156. data/book/script/authors +0 -1
  157. data/book/script/changelog +0 -1
  158. data/book/script/license +0 -1
  159. data/book/script/readme +0 -1
  160. data/book/text/ref_config.glyph +0 -100
  161. data/book/text/ref_macros.glyph +0 -6
  162. data/book/text/troubleshooting/errors_intro.glyph +0 -3
@@ -0,0 +1,16 @@
1
+ document[
2
+ head[
3
+ style[default.css]
4
+ ]
5
+ body[
6
+ @class[topic]
7
+ halftitlepage[
8
+ title[]
9
+ subtitle[]
10
+ author[]
11
+ ]
12
+ frontmatter[
13
+ toc[]
14
+ ]
15
+ ]
16
+ ]
@@ -0,0 +1,15 @@
1
+ document[
2
+ head[
3
+ style[default.css]
4
+ ]
5
+ body[
6
+ @class[topic]
7
+ section[
8
+ @title[{{title}}]
9
+ @id[{{id}}]
10
+ navigation[{{id}}]
11
+ {{contents}}
12
+ navigation[{{id}}]
13
+ ]
14
+ ]
15
+ ]
@@ -0,0 +1,16 @@
1
+ document[
2
+ head[
3
+ style[default.css]
4
+ ]
5
+ body[
6
+ @class[topic]
7
+ halftitlepage[
8
+ title[]
9
+ subtitle[]
10
+ author[]
11
+ ]
12
+ frontmatter[
13
+ toc[]
14
+ ]
15
+ ]
16
+ ]
@@ -0,0 +1,17 @@
1
+ document[
2
+ head[
3
+ style[default.css]
4
+ ]
5
+ body[
6
+ @class[topic]
7
+ =article[
8
+ section[
9
+ @title[{{title}}]
10
+ @id[{{id}}]
11
+ navigation[{{id}}]
12
+ {{contents}}
13
+ navigation[{{id}}]
14
+ ]
15
+ ]
16
+ ]
17
+ ]
data/lib/glyph.rb CHANGED
@@ -25,18 +25,25 @@ module Glyph
25
25
  TASKS_DIR = Pathname(__FILE__).dirname.expand_path/'../tasks'
26
26
 
27
27
  require LIB/'system_extensions'
28
+ require LIB/'utils'
28
29
  require LIB/'config'
29
30
  require LIB/'node'
31
+ require LIB/'bookmark'
30
32
  require LIB/'document'
31
33
  require LIB/'macro_validators'
34
+ require LIB/'macro_helpers'
32
35
  require LIB/'macro'
33
36
  require LIB/'syntax_node'
34
37
  require LIB/'parser'
35
38
  require LIB/'interpreter'
39
+ require LIB/'analyzer'
40
+ require LIB/'reporter'
41
+ extend Glyph::Utils
36
42
 
37
43
  class Error < RuntimeError; end
38
44
  class SyntaxError < Error; end
39
45
  class MacroError < Error
46
+ include Glyph::Utils
40
47
  attr_reader :macro
41
48
 
42
49
  # Initializes a new Glyph::MacroError
@@ -49,9 +56,10 @@ module Glyph
49
56
 
50
57
  # Displays the error message, source, path and node value (if debugging)
51
58
  def display
52
- Glyph.warning exception.message
53
- Glyph.msg " source: #{@macro.source}\n path: #{@macro.path}"
54
- Glyph.msg "#{"-"*54}\n#{@macro.node.to_s.gsub(/\t/, ' ')}\n#{"-"*54}" if Glyph.debug?
59
+ warning exception.message
60
+ path = @macro.path.blank? ? "" : "\n path: #{@macro.path}"
61
+ msg " source: #{@macro.source_name}#{path}"
62
+ msg "#{"-"*54}\n#{@macro.node.to_s.gsub(/\t/, ' ')}\n#{"-"*54}" if Glyph.debug?
55
63
  end
56
64
  end
57
65
  class MutualInclusionError < MacroError; end
@@ -65,6 +73,9 @@ module Glyph
65
73
  # All the currently-loaded macros
66
74
  MACROS = {}
67
75
 
76
+ # All macro aliases
77
+ ALIASES = {:by_alias => {}, :by_def => {}}
78
+
68
79
  begin
69
80
  unless const_defined? :MODE then
70
81
  MODE = {
@@ -139,6 +150,7 @@ module Glyph
139
150
  # Restores Glyph configuration (keeping all overrides and project settings)
140
151
  def self.config_refresh
141
152
  CONFIG.merge!(SYSTEM_CONFIG.merge(GLOBAL_CONFIG.merge(PROJECT_CONFIG)))
153
+ Glyph.safe_mode = Glyph['options.safe_mode']
142
154
  end
143
155
 
144
156
  # Resets Glyph configuration (removing all overrides and project settings)
@@ -148,13 +160,6 @@ module Glyph
148
160
  self.config_refresh
149
161
  end
150
162
 
151
- # Returns true if the PROJECT constant is set to a valid Glyph project directory
152
- def self.project?
153
- children = ["text", "output", "snippets.yml", "config.yml", "document.glyph"].sort
154
- actual_children = PROJECT.children.map{|c| c.basename.to_s}.sort
155
- (actual_children & children) == children
156
- end
157
-
158
163
  # Resets Glyph completely, i.e.:
159
164
  # * Re-enables all Glyph Rake tasks
160
165
  # * Resets the configuration to system defaults
@@ -198,9 +203,22 @@ module Glyph
198
203
  MACROS[name.to_sym] = block
199
204
  end
200
205
 
201
- def self.rewrite(name, &block)
202
- MACROS[name.to_sym] = lambda do
203
- rewrite &block
206
+ #@since 0.4.0
207
+ # Creates a new macro by rewriting.
208
+ # @param [Symbol, String] name the name of the macro
209
+ # @param [String] text the Glyph code used to define the macro
210
+ def self.rewrite(name, text)
211
+ macro name do
212
+ body = text.dup
213
+ # Parameters
214
+ body.gsub!(/\{\{(\d+)\}\}/) do
215
+ raw_param($1.to_i).strip
216
+ end
217
+ # Attributes
218
+ body.gsub!(/\{\{([^\[\]\|\\\s]+)\}\}/) do
219
+ raw_attr($1.to_sym).strip
220
+ end
221
+ interpret body
204
222
  end
205
223
  end
206
224
 
@@ -210,12 +228,11 @@ module Glyph
210
228
  # {:old_name => :new_name}
211
229
  def self.macro_alias(pair)
212
230
  name = pair.keys[0].to_sym
213
- found = MACROS[name]
214
- if found then
215
- self.warning "Invalid alias: macro '#{name}' already exists."
216
- return
217
- end
218
- MACROS[name] = MACROS[pair.values[0].to_sym]
231
+ orig = pair.values[0].to_sym
232
+ ALIASES[:by_def][orig] = [] unless ALIASES[:by_def][orig]
233
+ ALIASES[:by_def][orig] << name unless ALIASES[:by_def][orig].include? name
234
+ ALIASES[:by_alias][name] = orig
235
+ MACROS[name] = MACROS[orig]
219
236
  end
220
237
 
221
238
  # Compiles a single Glyph file
@@ -267,36 +284,6 @@ module Glyph
267
284
  end
268
285
 
269
286
 
270
- # Prints a message
271
- # @param [String] message the message to print
272
- def self.msg(message)
273
- puts message unless Glyph['system.quiet']
274
- end
275
-
276
- # Prints an informational message
277
- # @param [String] message the message to print
278
- def self.info(message)
279
- puts "-- #{message}" unless Glyph['system.quiet']
280
- end
281
-
282
- # Prints a warning
283
- # @param [String] message the message to print
284
- def self.warning(message)
285
- puts "-> warning: #{message}" unless Glyph['system.quiet']
286
- end
287
-
288
- # Prints an error
289
- # @param [String] message the message to print
290
- def self.error(message)
291
- puts "=> error: #{message}" unless Glyph['system.quiet']
292
- end
293
-
294
- # Prints a message if running in debug mode
295
- # @param [String] message the message to print
296
- def self.debug(message)
297
- puts message if Glyph.debug?
298
- end
299
-
300
287
  end
301
288
 
302
289
  Glyph.setup
@@ -0,0 +1,253 @@
1
+ module Glyph
2
+
3
+ # @since 0.4.0
4
+ # This class is used to collect statistics about a Glyph document.
5
+ class Analyzer
6
+
7
+ include Glyph::Utils
8
+
9
+ attr_reader :stats
10
+
11
+ # Initializes a new Analyzer
12
+ # @param [Glyph::Document] doc the document to analyze
13
+ def initialize(doc=Glyph.document)
14
+ @doc = doc
15
+ @stats = {}
16
+ @macros = []
17
+ @macros_by_def = {}
18
+ end
19
+
20
+ # Helper method used to return an array of specific macro instances
21
+ # @param [String, Symbol] name the name of the macro definition
22
+ def macro_array_for(name)
23
+ return @macros_by_def[name.to_sym] if @macros_by_def[name.to_sym]
24
+ key = @macros_by_def.keys.select{|k| macro_eq?(k, name.to_sym) }[0] || name.to_sym
25
+ @macros_by_def[key] = [] unless @macros_by_def[key]
26
+ @macros_by_def[key]
27
+ end
28
+
29
+ # Iterator over specific macro instances
30
+ # @param [String, Symbol] name the name of the macro definition (if left blank, iterates over all macro instances)
31
+ # @yieldparam [Glyph::MacroNode] n the macro node
32
+ def with_macros(name=nil, &block)
33
+ raise ArgumentError, "No block given" unless block_given?
34
+ name = name.to_sym if name
35
+ if !name then
36
+ unless @macros.blank? then
37
+ @macros.each(&block)
38
+ else
39
+ @doc.structure.descend do |n, level|
40
+ if n.is_a?(Glyph::MacroNode) && n.source
41
+ @macros << n
42
+ macro_array_for(n[:name]) << n
43
+ block.call n
44
+ end
45
+ end
46
+ end
47
+ else
48
+ existing = @macros_by_def[name]
49
+ if existing then
50
+ existing.each(&block)
51
+ else
52
+ macros = []
53
+ @doc.structure.descend do |n, level|
54
+ if n.is_a?(Glyph::MacroNode) && macro_eq?(name, n[:name]) && n.source
55
+ macros << n
56
+ block.call n
57
+ end
58
+ end
59
+ @macros_by_def[name] = macros
60
+ end
61
+ end
62
+ end
63
+
64
+ # Retrieves statistics of a given type
65
+ # @param [String, Symbol] stats_type the type of stats to retrieve
66
+ # (:macros, :bookmarks, :links, :snippets, :files, :global, :macro, :bookmark, :snippet, :link)
67
+ # @param [String, Symbol] *args Stats parameter(s) (e.g. a macro name, bookmark ID, etc.)
68
+ def stats_for(stats_type, *args)
69
+ begin
70
+ send :"stats_#{stats_type}", *args
71
+ rescue NoMethodError => e
72
+ raise RuntimeError, "Unable to calculate #{stats_type} stats"
73
+ end
74
+ end
75
+
76
+ protected
77
+
78
+ def stats_global
79
+ stats_macros
80
+ stats_bookmarks
81
+ stats_links
82
+ stats_snippets
83
+ stats_files
84
+ end
85
+
86
+ def stats_macros
87
+ c = @stats[:macros] = {}
88
+ c[:aliases] = Glyph::ALIASES[:by_alias].keys.sort
89
+ c[:definitions] = (Glyph::MACROS.keys - c[:aliases]).uniq.sort
90
+ c[:instances] = []
91
+ with_macros {|n| c[:instances] << n[:name]}
92
+ c[:used_definitions] = c[:instances].map{|m| macro_definition_for(m) || m}.uniq.sort
93
+ end
94
+
95
+ def stats_macro(name)
96
+ name = name.to_sym
97
+ raise ArgumentError, "Unknown macro '#{name}'" unless Glyph::MACROS.include? name
98
+ c = @stats[:macro] = {}
99
+ c[:param] = name
100
+ c[:instances] = []
101
+ c[:alias_for] = macro_definition_for name
102
+ files = {}
103
+ with_macros(name) do |n|
104
+ unless n.source.blank? then
105
+ c[:instances] << n[:name]
106
+ files[n.source] ||= 0
107
+ files[n.source] += 1
108
+ end
109
+ end
110
+ raise ArgumentError, "Macro '#{name}' is not used in this document" if c[:instances].blank?
111
+ if c[:alias_for] && !name.in?(c[:instances]) then
112
+ raise ArgumentError, "Macro '#{name}' is not used in this document, did you mean '#{c[:alias_for]}'?"
113
+ end
114
+ c[:files] = files.to_a.sort
115
+ end
116
+
117
+ def stats_bookmarks
118
+ c = @stats[:bookmarks] = {}
119
+ c[:codes] = []
120
+ files = {}
121
+ @doc.bookmarks.each_pair do |k, v|
122
+ c[:codes] << k
123
+ files[v.file] ||= 0
124
+ files[v.file] +=1
125
+ end
126
+ c[:codes].sort!
127
+ referenced = {}
128
+ with_macros(:link) do |n|
129
+ target =n.parameters[0][:value].to_s
130
+ if target.match(/^#/) then
131
+ code = target.gsub(/^#/, '').to_sym
132
+ referenced[code] ||= 0
133
+ referenced[code] += 1
134
+ end
135
+ end
136
+ c[:referenced] = referenced.to_a.sort
137
+ c[:unreferenced] = c[:codes] - c[:referenced].map{|e| e[0]}
138
+ end
139
+
140
+ def stats_bookmark(name)
141
+ code = name.to_s.gsub(/^#/, '').to_sym
142
+ raise ArgumentError, "Bookmark '#{code}' does not exist" unless @doc.bookmark? code
143
+ c = @stats[:bookmark] = {}
144
+ bmk = @doc.bookmark? code
145
+ c[:param] = name
146
+ c[:definition] = bmk.definition.to_s
147
+ c[:file] = bmk.file.to_s
148
+ c[:type] = bmk.is_a?(Glyph::Header) ? :header : :anchor
149
+ references = {}
150
+ with_macros(:link) do |n|
151
+ target = n.parameters[0].to_s.gsub(/^#/, '').to_sym
152
+ count_occurrences_for references, target, n if target == code
153
+ end
154
+ c[:references]= references[code][:files] rescue []
155
+ end
156
+
157
+ def stats_links
158
+ c = @stats[:links] = {}
159
+ internal = {}
160
+ external = {}
161
+ with_macros(:link) do |n|
162
+ target = n.parameters[0].to_s
163
+ collection = target.match(/^#/) ? internal : external
164
+ #code = target.gsub(/^#/, '').to_sym
165
+ count_occurrences_for collection, target, n
166
+ end
167
+ c[:internal] = internal.to_a.sort
168
+ c[:external] = external.to_a.sort
169
+ end
170
+
171
+ def stats_link(name)
172
+ regexp = /#{name}/
173
+ links = {}
174
+ with_macros(:link) do |n|
175
+ target = n.parameters[0].to_s
176
+ if target.match regexp then
177
+ count_occurrences_for links, target, n
178
+ end
179
+ end
180
+ raise ArgumentError, "No link matching /#{name}/ was found" if links.blank?
181
+ @stats[:link] = {}
182
+ @stats[:link][:stats] = links.to_a.sort
183
+ @stats[:link][:param] = name
184
+ end
185
+
186
+ def stats_snippets
187
+ c = @stats[:snippets] = {}
188
+ snippets = {}
189
+ c[:definitions] = Glyph::SNIPPETS.keys.sort
190
+ c[:used] = []
191
+ c[:unused] = []
192
+ c[:total] = 0
193
+ with_macros(:snippet) do |n|
194
+ code = n.parameters[0].to_s.to_sym
195
+ c[:used] << code unless c[:used].include? code
196
+ c[:total] += 1
197
+ #count_occurrences_for snippets, code, n
198
+ end
199
+ #c[:used_details] = snippets.to_a.sort
200
+ c[:used].sort!
201
+ c[:unused] = (c[:definitions] - c[:used]).sort
202
+ end
203
+
204
+ def stats_snippet(name)
205
+ name = name.to_sym
206
+ snippets = {}
207
+ raise ArgumentError, "Snippet '#{name}' does not exist" unless Glyph::SNIPPETS[name]
208
+ with_macros(:snippet) do |n|
209
+ code = n.parameters[0].to_s.to_sym
210
+ if code == name then
211
+ count_occurrences_for snippets, code, n
212
+ end
213
+ end
214
+ raise ArgumentError, "Snippet '#{name}' is not used in this document" if snippets.blank?
215
+ @stats[:snippet] = {}
216
+ @stats[:snippet][:stats] = snippets[name]
217
+ @stats[:snippet][:param] = name
218
+ end
219
+
220
+ def stats_files
221
+ @stats[:files] = {}
222
+ count_files_in = lambda do |dir|
223
+ files = []
224
+ (Glyph::PROJECT/"#{dir}").find{|f| files << f if f.file? } rescue nil
225
+ files.length
226
+ end
227
+ @stats[:files][:text] = count_files_in.call 'text'
228
+ @stats[:files][:lib] = count_files_in.call 'lib'
229
+ @stats[:files][:styles] = count_files_in.call 'styles'
230
+ @stats[:files][:layouts] = count_files_in.call 'layouts'
231
+ @stats[:files][:images] = count_files_in.call 'images'
232
+ end
233
+
234
+ private
235
+
236
+ def count_occurrences_for(collection, code, n)
237
+ collection[code] ||= {:total => 0, :files =>[]} unless collection[code]
238
+ collection[code][:total] += 1
239
+ coll = collection[code][:files]
240
+ added = false
241
+ coll.each do |file|
242
+ if file[0] == n.source then
243
+ file[1] += 1
244
+ added = true
245
+ break
246
+ end
247
+ end
248
+ coll << [n.source, 1] unless added
249
+ coll.sort!
250
+ end
251
+
252
+ end
253
+ end