glyph 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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,6 @@
1
+ GLI.desc 'Create a new Glyph project'
2
+ command :init do |c|
3
+ c.action do |global_options,options,args|
4
+ Glyph.run 'project:create', Dir.pwd
5
+ end
6
+ end
@@ -0,0 +1,45 @@
1
+ GLI.desc 'Display the document outline'
2
+ command :outline do |c|
3
+ c.desc "Limit to level N"
4
+ c.flag :l, :level
5
+ c.desc "Show file names"
6
+ c.switch :f, :files
7
+ c.desc "Show titles"
8
+ c.switch :t, :titles
9
+ c.desc "Show IDs"
10
+ c.switch :i, :ids
11
+ c.action do |global_options, options, args|
12
+ levels = options[:l]
13
+ ids = options[:i]
14
+ files = options[:f]
15
+ titles = options[:t]
16
+ titles = true if !ids && !levels && !files || levels && !ids
17
+ Glyph['system.quiet'] = true
18
+ Glyph.run "generate:document"
19
+ Glyph['system.quiet'] = false
20
+ puts "====================================="
21
+ puts "#{Glyph['document.title']} - Outline"
22
+ puts "====================================="
23
+ Glyph.document.structure.descend do |n, level|
24
+ if n.is_a?(Glyph::MacroNode) then
25
+ case
26
+ when n[:name].in?(Glyph['system.structure.headers']) then
27
+ header = Glyph.document.header?(n[:header].code) rescue nil
28
+ next if !header || levels && header.level-1 > levels.to_i
29
+ last_level = header.level
30
+ h_id = ids ? "[##{header.code}]" : ""
31
+ h_title = titles ? "#{header.title} " : ""
32
+ text = (" "*(header.level-1))+"- "+h_title+h_id
33
+ puts text unless text.blank?
34
+ when n[:name] == :include then
35
+ if files && n.find_parent{|p| p[:name] == :document && p.is_a?(Glyph::MacroNode)} then
36
+ # When using the book or article macros, includes appear twice:
37
+ # * in the macro parameters
38
+ # * as children of the document macro
39
+ puts "=== #{n.param(0)}"
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,48 @@
1
+ GLI.desc 'Display statistics'
2
+ command :stats do |c|
3
+ c.desc "Display stats about macros"
4
+ c.switch [:m, :macros]
5
+ c.desc "Display stats about snippets"
6
+ c.switch [:s, :snippets]
7
+ c.desc "Display stats about bookmarks"
8
+ c.switch [:b, :bookmarks]
9
+ c.desc "Display stats about links"
10
+ c.switch [:l, :links]
11
+ c.desc "Display stats about project files"
12
+ c.switch [:f, :files]
13
+ c.desc "Display stats about a single macro"
14
+ c.flag :macro
15
+ c.desc "Display stats about a single bookmark"
16
+ c.flag :bookmark
17
+ c.desc "Display stats about links matching a regular expression"
18
+ c.flag :link
19
+ c.desc "Display stats about a single snippet"
20
+ c.flag :snippet
21
+ c.action do |global_options, options, args|
22
+ Glyph.info "Collecting stats..."
23
+ Glyph.run 'generate:document'
24
+ analyzer = Glyph::Analyzer.new
25
+ no_switches = true
26
+ [[:m, :macros], [:s, :snippets], [:b, :bookmarks], [:l, :links], [:f, :files]].each do |s|
27
+ if options[s[0]] then
28
+ analyzer.stats_for s[1]
29
+ no_switches = false
30
+ end
31
+ end
32
+ no_flags = true
33
+ [:macro, :bookmark, :link, :snippet].each do |f|
34
+ if options[f] then
35
+ analyzer.stats_for f, options[f]
36
+ no_flags = false
37
+ end
38
+ end
39
+ analyzer.stats_for :global if no_switches && no_flags
40
+ puts "====================================="
41
+ puts "#{Glyph['document.title']} - Statistics"
42
+ puts "====================================="
43
+ puts
44
+ reporter = Glyph::Reporter.new(analyzer.stats)
45
+ reporter.detailed = false if no_switches && no_flags
46
+ reporter.display
47
+ end
48
+ end
@@ -0,0 +1,29 @@
1
+ GLI.desc 'Display all project TODO items'
2
+ command :todo do |c|
3
+ c.action do |global_options, options, args|
4
+ Glyph['system.quiet'] = true
5
+ Glyph.run "generate:document"
6
+ Glyph['system.quiet'] = false
7
+ unless Glyph.document.todos.blank?
8
+ puts "====================================="
9
+ puts "#{Glyph['document.title']} - TODOs"
10
+ puts "====================================="
11
+ # Group items
12
+ if Glyph.document.todos.respond_to? :group_by then
13
+ Glyph.document.todos.group_by{|e| e[:source]}.each_pair do |k, v|
14
+ puts
15
+ puts "=== #{k} "
16
+ v.each do |i|
17
+ puts " * #{i[:text]}"
18
+ end
19
+ end
20
+ else
21
+ Glyph.document.todos.each do |t|
22
+ Glyph.info t
23
+ end
24
+ end
25
+ else
26
+ Glyph.info "Nothing left to do."
27
+ end
28
+ end
29
+ end
data/lib/glyph/config.rb CHANGED
@@ -4,6 +4,8 @@ module Glyph
4
4
  # and provides some useful methods to access keys and subkeys.
5
5
  class Config
6
6
 
7
+ include Glyph::Utils
8
+
7
9
  # Initializes the configuration with a hash of options:
8
10
  # * :file (default: nil) - A YAML file to read data from
9
11
  # * :data (default: {})- The initial contents
@@ -18,24 +18,28 @@ module Glyph
18
18
  ['\\|', '|']
19
19
  ]
20
20
 
21
- attr_reader :bookmarks, :placeholders, :headers, :context, :errors, :todos
21
+ attr_reader :bookmarks, :placeholders, :headers, :styles, :context, :errors, :todos, :topics, :links, :toc
22
22
 
23
23
  # Creates a new document
24
24
  # @param [GlyphSyntaxNode] tree the syntax tree to be evaluate
25
25
  # @param [Glyph::Node] context the context associated with the tree
26
26
  # @raise [RuntimeError] unless tree responds to :evaluate
27
- def initialize(tree, context)
27
+ def initialize(tree, context={})
28
28
  @tree = tree
29
29
  @context = context
30
- @bookmarks = {}
30
+ @context[:source] ||= {:file => nil, :name => '--', :topic => nil}
31
31
  @placeholders = {}
32
- @headers = []
32
+ @bookmarks = {}
33
+ @headers = {}
34
+ @styles = []
33
35
  @errors = []
34
36
  @todos = []
37
+ @topics = []
38
+ @links = []
39
+ @toc = {}
35
40
  @state = :new
36
41
  end
37
42
 
38
-
39
43
  # Returns a tree of Glyph::Node objects corresponding to the analyzed document
40
44
  # @raise [RuntimeError] unless the document has been analized
41
45
  def structure
@@ -43,13 +47,18 @@ module Glyph
43
47
  @tree
44
48
  end
45
49
 
46
- # Copies bookmarks, headers, todos and placeholders from another Glyph::Document
50
+ # Copies bookmarks, headers, todos, styles and placeholders from another Glyph::Document
47
51
  # @param [Glyph::Document] document a valid Glyph::Document
48
52
  def inherit_from(document)
49
53
  @bookmarks = document.bookmarks
50
54
  @headers = document.headers
51
55
  @todos = document.todos
56
+ @styles = document.styles
57
+ @topics = document.topics
52
58
  @placeholders = document.placeholders
59
+ @toc = document.toc
60
+ @links = document.links
61
+ self
53
62
  end
54
63
 
55
64
  # Defines a placeholder block that will be evaluated after the whole document has been analyzed
@@ -63,34 +72,51 @@ module Glyph
63
72
 
64
73
  # Returns a stored bookmark or nil
65
74
  # @param [#to_sym] key the bookmark identifier
66
- # @return [Hash, nil] the bookmark hash or nil if no bookmark is found
75
+ # @return [Glyph::Bookmark, nil] the bookmark or nil if no bookmark is found
67
76
  def bookmark?(key)
68
77
  @bookmarks[key.to_sym]
69
78
  end
70
79
 
71
80
  # Stores a new bookmark
72
- # @param [Hash] hash the bookmark hash: {:id => "Bookmark ID", :title => "Bookmark Title"}
73
- # @return [Hash] the stored bookmark (:id is converted to a symbol)
81
+ # @param [Hash] hash the bookmark hash: {:id => "BookmarkID", :title => "Bookmark Title", :file => "dir/preface.glyph"}
82
+ # @return [Glyph::Bookmark] the stored bookmark
83
+ # @raise [RuntimeError] if the bookmark is already defined.
74
84
  def bookmark(hash)
75
- ident = hash[:id].to_sym
76
- hash[:id] = ident
77
- @bookmarks[ident] = hash
78
- hash
85
+ b = Glyph::Bookmark.new(hash)
86
+ raise RuntimeError, "Bookmark '#{b.code}' already exists" if @bookmarks.has_key? b.code
87
+ @bookmarks[b.code] = b
88
+ b
79
89
  end
80
90
 
81
91
  # Stores a new header
82
- # @param [Hash] hash the header hash: {:id => "Bookmark ID", :title => "Bookmark Title", :level => 3}
83
- # @return [Hash] the stored header
92
+ # @param [Hash] hash the header hash: {:id => "Bookmark_ID", :title => "Bookmark Title", :level => 3}
93
+ # @return [Glyph::Header] the stored header
94
+ # @raise [RuntimeError] if the bookmark is already defined.
84
95
  def header(hash)
85
- @headers << hash
86
- hash
96
+ b = Glyph::Header.new(hash)
97
+ raise RuntimeError, "Bookmark '#{b.code}' already exists" if @bookmarks.has_key? b.code
98
+ @bookmarks[b.code] = b
99
+ @headers[b.code] = b
100
+ b
87
101
  end
88
102
 
89
103
  # Returns a stored header or nil
90
- # @param [String] key the header identifier
91
- # @return [Hash, nil] the header hash or nil if no header is found
104
+ # @param [String, Symbol] key the header identifier
105
+ # @return [Glyph::Header, nil] the header or nil if no header is found
92
106
  def header?(key)
93
- @headers.select{|h| h[:id] == key}[0] rescue nil
107
+ @headers[key.to_sym]
108
+ end
109
+
110
+ # @since 0.4.0
111
+ # Stores a stylesheet
112
+ # @param [String] file the stylesheet file
113
+ # @raises [RuntimeError] if the stylesheet is already specified for the document (unless the output has more than one file)
114
+ def style(file)
115
+ f = Pathname.new file
116
+ if @styles.include?(f) && !Glyph.multiple_output_files? then
117
+ raise RuntimeError, "Stylesheet '#{f}' already specified for the current document"
118
+ end
119
+ @styles << f
94
120
  end
95
121
 
96
122
  # Analyzes the document by evaluating its @tree
@@ -114,18 +140,23 @@ module Glyph
114
140
  raise RuntimeError, "Document cannot be finalized due to previous errors" unless @context[:document].errors.blank?
115
141
  # Substitute placeholders
116
142
  ESCAPES.each{|e| @output.gsub! e[0], e[1]}
117
- begin
118
- @placeholders.each_pair do |key, value|
119
- begin
120
- @output.gsub! key.to_s, value.call(self).to_s
121
- rescue Glyph::MacroError => e
122
- e.macro.macro_warning e.message, e
123
- rescue Exception => e
124
- puts e.class
125
- Glyph.warning e.message
143
+ @placeholders.each_pair do |key, value|
144
+ begin
145
+ key_s = key.to_s
146
+ value_s = value.call(self).to_s
147
+ toc[:contents].gsub! key_s, value_s if toc[:contents].to_s.match key_s
148
+ if Glyph.multiple_output_files? then
149
+ @topics.each do |t|
150
+ ESCAPES.each{|e| t[:contents].gsub! e[0], e[1]}
151
+ t[:contents].gsub! key_s, value_s
152
+ end
126
153
  end
154
+ @output.gsub! key_s, value_s
155
+ rescue Glyph::MacroError => e
156
+ e.macro.macro_warning e.message, e
157
+ rescue Exception => e
158
+ Glyph.warning e.message
127
159
  end
128
- rescue Exception => e
129
160
  end
130
161
  @state = :finalized
131
162
  end
@@ -11,7 +11,7 @@ module Glyph
11
11
  # @param [Hash] context the context to pass along when expanding macros
12
12
  def initialize(text, context={})
13
13
  @context = context
14
- @context[:source] ||= {:name => "--"}
14
+ @context[:source] ||= {:name => "--", :file => nil, :topic => nil}
15
15
  @text = text
16
16
  @parser = Glyph::Parser.new text, @context[:source][:name]
17
17
  end
@@ -47,7 +47,7 @@ module Glyph
47
47
  @document.inherit_from @context[:document] if @context[:document]
48
48
  @tree
49
49
  end
50
-
50
+
51
51
  end
52
52
  end
53
53
 
data/lib/glyph/macro.rb CHANGED
@@ -8,8 +8,10 @@ module Glyph
8
8
  class Macro
9
9
 
10
10
  include Validators
11
+ include Helpers
12
+ include Utils
11
13
 
12
- attr_reader :node, :source
14
+ attr_reader :node, :source_name, :source_file, :source_topic
13
15
 
14
16
  # Creates a new macro instance from a Node
15
17
  # @param [Node] node a node populated with macro data
@@ -17,14 +19,20 @@ module Glyph
17
19
  @node = node
18
20
  @name = @node[:name]
19
21
  @updated_source = nil
20
- @source = @node[:source][:name] rescue "--"
22
+ @source_name = @node[:source][:name] || nil rescue "--"
23
+ @source_topic = @node[:source][:topic] || nil rescue "--"
24
+ @source_file = @node[:source][:file] rescue nil
21
25
  end
22
26
 
23
27
  # Resets the name of the updated source (call before calling
24
28
  # Macro#interpret)
25
29
  # @param [String] name the source name
26
- def update_source(name)
27
- @updated_source = {:node => @node, :name => name}
30
+ # @param [String] file the source file
31
+ # @param [String] topic the topic file
32
+ # @since 0.3.0
33
+ def update_source(name, file=nil, topic=nil)
34
+ file ||= @node[:source][:file] rescue nil
35
+ @updated_source = {:name => name, :file => file, :topic => topic}
28
36
  end
29
37
 
30
38
  # Returns a Glyph code representation of the specified parameter
@@ -180,7 +188,7 @@ module Glyph
180
188
  if e.is_a?(Glyph::MacroError) then
181
189
  e.display
182
190
  else
183
- message = "#{msg}\n source: #{@source}\n path: #{path}"
191
+ message = "#{msg}\n source: #{@source_name}\n path: #{path}"
184
192
  if Glyph.debug? then
185
193
  message << %{\n#{"-"*54}\n#{@node.to_s.gsub(/\t/, ' ')}\n#{"-"*54}}
186
194
  if e then
@@ -206,6 +214,7 @@ module Glyph
206
214
  context[:document] = @node[:document]
207
215
  interpreter = Glyph::Interpreter.new string, context
208
216
  subtree = interpreter.parse
217
+ subtree[:source] = context[:source]
209
218
  @node << subtree
210
219
  result = interpreter.document.output
211
220
  end
@@ -0,0 +1,280 @@
1
+ module Glyph
2
+ class Macro
3
+
4
+ # This module includes some output-agnostic methods used by the most common Glyph macros.
5
+ # @since 0.4.0
6
+ module Helpers
7
+
8
+ # Renders a link
9
+ # @param [String] target the target of the link
10
+ # @param[String] title the title of the link
11
+ # @yield [link_path, link_title] the block to call to render the link
12
+ # @yieldparam [String] link_path the path to the link target
13
+ # @yieldparam [String] link_title the title of the link
14
+ def link_element_for(target, title, &block)
15
+ if target.match /^#/ then
16
+ @node[:document].links << target
17
+ anchor = target.gsub /^#/, ''
18
+ bmk = bookmark? anchor
19
+ if !bmk then
20
+ placeholder do |document|
21
+ bmk = document.bookmark?(anchor)
22
+ macro_error "Bookmark '#{anchor}' does not exist" unless bmk
23
+ bmk_title = title
24
+ bmk_title = bmk.title if bmk_title.blank?
25
+ block.call bmk.link(@source_file), bmk_title
26
+ end
27
+ else
28
+ bmk_title = title
29
+ bmk_title = bmk.title if bmk_title.blank?
30
+ block.call bmk.link(@source_file), bmk_title
31
+ end
32
+ else
33
+ if Glyph['options.url_validation'] && !@node[:document].links.include?(target) then
34
+ begin
35
+ url = URI.parse(target.gsub(/\\\./, ''))
36
+ rescue Exception => e
37
+ macro_warning "Invalid URL: #{url||target}", e
38
+ end
39
+ response = Net::HTTP.get_response(url)
40
+ debug "Checking link URL: #{url} (#{response.code})"
41
+ if response.code.to_i > 302 then
42
+ macro_warning "Linked URL '#{url}' returned status #{response.code} (#{response.message})"
43
+ end
44
+ end
45
+ @node[:document].links << target
46
+ title ||= target
47
+ block.call target, title
48
+ end
49
+ end
50
+
51
+ # Renders a For More Information note
52
+ # @param [String] topic the topic of the note
53
+ # @param [String] href the reference to link to
54
+ # @yield [topic, link] the block used to render the FMI note
55
+ def fmi_element_for(topic, href, &block)
56
+ link = placeholder do |document|
57
+ interpret "link[#{href}]"
58
+ end
59
+ block.call topic, link
60
+ end
61
+
62
+ # Renders a draft comment element
63
+ # @yield [value] the block used to render the comment
64
+ # @yieldparam [String] value the comment text
65
+ def draftcomment_element(&block)
66
+ if Glyph['document.draft'] then
67
+ block.call value
68
+ else
69
+ ""
70
+ end
71
+ end
72
+
73
+ # Renders a todo element
74
+ # @yield [value] the block used to render the todo element
75
+ # @yieldparam [String] value the todo text
76
+ def todo_element(&block)
77
+ todo = {:source => @source_name, :text => value}
78
+ @node[:document].todos << todo unless @node[:document].todos.include? todo
79
+ if Glyph['document.draft'] then
80
+ block.call value
81
+ else
82
+ ""
83
+ end
84
+ end
85
+
86
+ # Renders an image element
87
+ # @param [String] image the image to render
88
+ # @param [String] alt the value of the image's ALT tag
89
+ # @yield [alt, dest_file] the block used to render the image
90
+ def image_element_for(image, alt, &block)
91
+ src_file = Glyph.lite? ? image : Glyph::PROJECT/"images/#{image}"
92
+ dest_file = Glyph.lite? ? image : "images/#{image}"
93
+ warning "Image '#{image}' not found" unless Pathname.new(src_file).exist?
94
+ block.call alt, dest_file
95
+ end
96
+
97
+ # Renders a figure element
98
+ # @param [String] image the image to render
99
+ # @param [String] alt the value of the image's ALT tag
100
+ # @param [String] caption
101
+ # @yield alt, dest_file, caption] the block used to render the figure
102
+ def figure_element_for(image, alt, caption, &block)
103
+ src_file = Glyph.lite? ? image : Glyph::PROJECT/"images/#{image}"
104
+ dest_file = Glyph.lite? ? image : "images/#{image}"
105
+ warning "Figure '#{image}' not found" unless Pathname.new(src_file).exist?
106
+ block.call alt, dest_file, caption
107
+ end
108
+
109
+ # Renders a title element
110
+ def title_element(&block)
111
+ unless Glyph["document.title"].blank? then
112
+ block.call
113
+ else
114
+ ""
115
+ end
116
+ end
117
+
118
+ # Renders a subtitle element
119
+ def subtitle_element(&block)
120
+ unless Glyph["document.subtitle"].blank? then
121
+ block.call
122
+ else
123
+ ""
124
+ end
125
+ end
126
+
127
+ # Renders an author element
128
+ def author_element(&block)
129
+ unless Glyph['document.author'].blank? then
130
+ block.call
131
+ else
132
+ ""
133
+ end
134
+ end
135
+
136
+ # Renders a revision element
137
+ def revision_element(&block)
138
+ unless Glyph["document.revision"].blank? then
139
+ block.call
140
+ else
141
+ ""
142
+ end
143
+ end
144
+
145
+ # Renders a Table of Contents
146
+ # @param [Integer] depth the maximum header level
147
+ # @param [String] title the title of the TOC
148
+ # @param [Hash] procs the Proc objects used to render the TOC
149
+ # @option procs [Proc] :link used to render TOC header links (parameters: Glyph::Header).
150
+ # @option procs [Proc] :toc_list used to render the TOC list (parameters: a Proc used to traverse the document tree, the Glyph::Bookmark used for the TOC header, a Glyph::Document)
151
+ # @option procs [Proc] :toc_item used to render a TOC item (parameters: an Array of header classes, a String used for the header link)
152
+ # @option procs [Proc] :toc_sublist used to render a TOC sublist (parameters: a String containing the contents of the list)
153
+ def toc_element_for(depth, title, procs={})
154
+ return @node[:document].toc[:contents] if @node[:document].toc[:contents]
155
+ link_header = procs[:link]
156
+ toc = placeholder do |document|
157
+ descend_section = lambda do |n1, added_headers|
158
+ list = ""
159
+ added_headers ||= []
160
+ n1.descend do |n2, level|
161
+ if n2.is_a?(Glyph::MacroNode) && Glyph['system.structure.headers'].include?(n2[:name]) then
162
+ if Glyph.multiple_output_files? then
163
+ # Only consider topics/booklets when building TOC for web/web5
164
+ next if !n2.attribute(:src) && n2.child_macros.select{|child| child.attribute(:src)}.blank?
165
+ end
166
+ next if n2.find_parent{|node| Glyph['system.structure.special'].include? node[:name] }
167
+ header_hash = n2[:header]
168
+ next if depth && header_hash && (header_hash.level-1 > depth.to_i) || header_hash && !header_hash.toc?
169
+ next if added_headers.include? header_hash
170
+ added_headers << header_hash
171
+ # Check if part of frontmatter, bodymatter or backmatter
172
+ container = n2.find_parent do |node|
173
+ node.is_a?(Glyph::MacroNode) &&
174
+ node[:name].in?([:frontmatter, :bodymatter, :appendix, :backmatter])
175
+ end[:name] rescue nil
176
+ list << procs[:toc_item].call([container, n2[:name]], link_header.call(header_hash)) if header_hash
177
+ child_list = ""
178
+ n2.children.each do |c|
179
+ child_list << descend_section.call(c, added_headers)
180
+ end
181
+ list << procs[:toc_sublist].call(child_list) unless child_list.blank?
182
+ end
183
+ end
184
+ list
185
+ end
186
+ title ||= "Table of Contents"
187
+ bmk = @node[:document].bookmark?(:toc) || bookmark(:id => :toc, :file => @source_file, :title => title)
188
+ procs[:toc_list].call descend_section, bmk, document
189
+ end
190
+ @node[:document].toc[:contents] = toc.to_s
191
+ toc
192
+ end
193
+
194
+ # Renders a section element
195
+ # @param [Hash] procs the Proc objects used to render the section
196
+ # @option procs [Proc] :title used to render the section header (parameters: the header level, the section ID, the section title)
197
+ # @option procs [Proc] :body used to render the section body (parameters: the section title, the section body)
198
+ def section_element_for(procs={})
199
+ h = ""
200
+ if attr(:title) then
201
+ level = 1
202
+ @node.ascend do |n|
203
+ break if n.respond_to?(:attribute) && n.attribute(:class) && n.attribute(:class).children.join.strip == "topic"
204
+ if n.is_a?(Glyph::MacroNode) && Glyph["system.structure.headers"].include?(n[:name]) then
205
+ level+=1
206
+ end
207
+ end
208
+ ident = (attr(:id) || "h_#{@node[:document].headers.length+1}").to_sym
209
+ # The bookmark is added when the section is first processed; therefore it will exist already when a topic layout is processed
210
+ bmk = @node[:document].bookmark?(ident)
211
+ bmk ||= header :title => attr(:title),
212
+ :level => level,
213
+ :id => ident,
214
+ :toc => !attr(:notoc),
215
+ :definition => @source_file,
216
+ :file => (attr(:src) || @source_file)
217
+ @node[:header] = bmk
218
+ h = procs[:title].call level, bmk, attr(:title)
219
+ end
220
+ if attr(:src) then
221
+ # Create topic
222
+ if Glyph.multiple_output_files?
223
+ topic_id = (attr(:id) || "t_#{@node[:document].topics.length}").to_sym
224
+ layout = attr(:layout) || Glyph["output.#{Glyph['document.output']}.layouts.topic"] || :topic
225
+ layout_name = "layout:#{layout}".to_sym
226
+ macro_error "Layout '#{layout}' not found" unless Glyph::MACROS[layout_name]
227
+ result = interpret %{#{layout_name}[
228
+ @title[#{attr(:title)}]
229
+ @id[#{topic_id}]
230
+ @contents[include[@topic[true]#{attr(:src)}]]
231
+ ]}
232
+ bmk = @node[:document].bookmark? topic_id
233
+ if bmk then
234
+ # Fix file for topic bookmark
235
+ @node[:document].bookmark?(topic_id).file = attr(:src)
236
+ else
237
+ bookmark :title => attr(:title), :id => topic_id, :file => attr(:src), :definition => @source_file
238
+ end
239
+ topic_src = attr(:src)
240
+ topic_src += ".glyph" unless topic_src.match /\..+$/
241
+ @node[:document].topics << {:src => topic_src, :title => attr(:title), :id => topic_id, :contents => result}
242
+ # Process section contents
243
+ procs[:body].call h, value
244
+ # Return nothing
245
+ nil
246
+ else
247
+ v = raw_value
248
+ @node.children.delete_if{|c| !c.is_a?(Glyph::AttributeNode)}
249
+ body = interpret "include[#{attr(:src)}]#{v}"
250
+ procs[:body].call h, body
251
+ end
252
+ else
253
+ procs[:body].call h, value
254
+ end
255
+ end
256
+
257
+ # Renders a navigation element
258
+ # @param [String] topic_id the ID of the current topic
259
+ # @param [Hash] procs the Proc objects used to render the navigation element
260
+ # @option procs [Proc] :previous the link to the previous topic
261
+ # @option procs [Proc] :next the link to the next topic
262
+ # @option procs [Proc] :contents the link to the document contents
263
+ def navigation_element_for(topic_id, procs={})
264
+ # Get the previous topic
265
+ previous_topic = @node[:document].topics.last
266
+ previous_link = procs[:previous].call previous_topic
267
+ # The next topic is not going to be available yet, use a placeholder
268
+ next_link = placeholder do |document|
269
+ current_topic = document.topics.select{|t| t[:id] == topic_id}[0] rescue nil
270
+ next_topic = document.topics[document.topics.index(current_topic)+1] rescue nil
271
+ procs[:next].call next_topic
272
+ end
273
+ contents_link = procs[:contents].call
274
+ procs[:navigation].call contents_link, previous_link, next_link
275
+ end
276
+
277
+
278
+ end
279
+ end
280
+ end