History

cleanup the code

Chaitanya Rahalkar committed 1 year ago

Commit b2188361 parent b7393f41 file · +180 −185Browse files

Unified · Split

lib/docs/filters/threejs/clean_html.rb+180−185
@@ -1,243 +1,238 @@
1module Docs1module Docs
2 class Threejs2 class Threejs
3 class CleanHtmlFilter < Filter3 class CleanHtmlFilter < Filter
4 PATTERNS = {
5 method_this: /\[method:this\s+([^\]]+)\]\s*\((.*?)\)/,
6 method_return: /\[method:([^\s\]]+)\s+([^\]]+)\]\s*\((.*?)\)/,
7 method_no_params: /\[method:([^\s\]]+)\s+([^\]]+)\](?!\()/,
8 property: /\[property:([^\]]+?)\s+([^\]]+?)\]/,
9 example_link: /\[example:([^\s\]]+)\s+([^\]]+)\]/,
10 external_link_text: /\[link:([^\s\]]+)\s+([^\]]+)\]/,
11 external_link: /\[link:([^\]]+)\]/,
12 page_link_text: /\[page:([^\]]+?)\s+([^\]]+?)\]/,
13 page_link: /\[page:([^\]]+?)\]/,
14 inline_code: /`([^`]+)`/,
15 name_placeholder: /\[name\]/,
16 constructor_param: /\[param:([^\]]+?)\s+([^\]]+?)\]/
17 }.freeze
18
4 def call19 def call
5 # Remove unnecessary elements20 remove_unnecessary_elements
21 wrap_code_blocks
22 process_sections
23 format_links
24 add_section_structure
25 format_notes
26 add_heading_attributes
27 doc
28 end
29
30 private
31
32 def remove_unnecessary_elements
6 css('head, script, style').remove33 css('head, script, style').remove
7 34 end
8 # Wrap code blocks with pre tags and add syntax highlighting35
36 def wrap_code_blocks
9 css('code').each do |node|37 css('code').each do |node|
10 unless node.parent.name == 'pre'38 next if node.parent.name == 'pre'
11 pre = node.wrap('<pre>')39 pre = node.wrap('<pre>')
12 pre['data-language'] = 'javascript'40 pre['data-language'] = pre['class'] = 'language-javascript'
13 pre['class'] = 'language-javascript'
14 end
15 end41 end
16 42 end
43
44 def process_sections
17 # Handle source links45 # Handle source links
18 css('h2').each do |node|46 css('h2').each do |node|
19 if node.content.strip == 'Source'47 next unless node.content.strip == 'Source'
20 content = node.next_element&.inner_html48 handle_source_link(node)
21 if content
22 # Clean up any existing formatting
23 content = content.gsub(/<[^>]+>/, '')
24 # Extract the path from the content
25 if content =~ /src\/(.*?)\.js/
26 path = "/#{$1}.js"
27 formatted_link = %Q(<a class="reference external" href="https://github.com/mrdoob/three.js/blob/master/src#{path}">src#{path}</a>)
28 node.next_element.inner_html = formatted_link if node.next_element
29 end
30 end
31 end
32 end49 end
33 50
34 # Handle method signatures51 # Handle method signatures and properties
35 css('h3').each do |node|52 css('h3').each do |node|
36 content = node.inner_html53 content = node.inner_html
37 54 content = handle_method_signatures(content)
38 # Handle [method:this methodName]( param1, param2, ... ) format55 content = handle_properties(content)
39 content = content.gsub(/\[method:this\s+([^\]]+)\]\s*\((.*?)\)/) do |match|
40 method_name, params_str = $1, $2
41
42 # Format parameters
43 params = params_str.split(',').map do |param|
44 param = param.strip
45 if param.include?(' ')
46 type, name = param.split(' ', 2).map(&:strip)
47 "<span class='sig-param'><span class='sig-type'>#{type}</span> <span class='sig-name'>#{name}</span></span>"
48 else
49 "<span class='sig-param'>#{param}</span>"
50 end
51 end.join("<span class='sig-paren'>, </span>")
52
53 "<dt class='sig sig-object js' id='#{method_name}'>" \
54 "<span class='property'><span class='pre'>this</span></span>." \
55 "<span class='sig-name descname'>#{method_name}</span>" \
56 "<span class='sig-paren'>(</span>" \
57 "#{params}" \
58 "<span class='sig-paren'>)</span></dt>"
59 end
60
61 # Handle [method:returnType methodName]( param1, param2, ... ) format
62 content = content.gsub(/\[method:([^\s\]]+)\s+([^\]]+)\]\s*\((.*?)\)/) do |match|
63 return_type, method_name, params_str = $1, $2, $3
64 next if method_name.start_with?('this') # Skip if already handled above
65
66 # Format parameters
67 params = params_str.split(',').map do |param|
68 param = param.strip
69 if param.include?(' ')
70 type, name = param.split(' ', 2).map(&:strip)
71 "<span class='sig-param'><span class='sig-type'>#{type}</span> <span class='sig-name'>#{name}</span></span>"
72 else
73 "<span class='sig-param'>#{param}</span>"
74 end
75 end.join("<span class='sig-paren'>, </span>")
76
77 "<dt class='sig sig-object js' id='#{method_name}'>" \
78 "<span class='sig-name descname'>#{method_name}</span>" \
79 "<span class='sig-paren'>(</span>" \
80 "#{params}" \
81 "<span class='sig-paren'>)</span>" \
82 "<span class='sig-returns'><span class='sig-colon'>:</span> " \
83 "<span class='sig-type'>#{return_type}</span></span></dt>"
84 end
85
86 # Handle [method:returnType methodName] format (no parameters)
87 content = content.gsub(/\[method:([^\s\]]+)\s+([^\]]+)\](?!\()/) do |match|
88 return_type, method_name = $1, $2
89 "<dt class='sig sig-object js' id='#{method_name}'>" \
90 "<span class='sig-name descname'>#{method_name}</span>" \
91 "<span class='sig-paren'>(</span>" \
92 "<span class='sig-paren'>)</span>" \
93 "<span class='sig-returns'><span class='sig-colon'>:</span> " \
94 "<span class='sig-type'>#{return_type}</span></span></dt>"
95 end
96
97 node.inner_html = content56 node.inner_html = content
98 end57 end
99 58
100 # Handle [name] placeholders in headers and constructor59 # Handle name placeholders and constructor params
101 css('h1, h3').each do |node|60 css('h1, h3').each do |node|
102 content = node.inner_html61 content = node.inner_html
103 62 content = handle_name_placeholders(content)
104 # Replace [name] with class name63 content = format_constructor_params(content)
105 content = content.gsub(/\[name\]/) do
106 name = slug.split('/').last.gsub('.html', '')
107 "<span class='descname'>#{name}</span>"
108 end
109
110 # Format constructor parameters
111 content = content.gsub(/\[param:([^\]]+?)\s+([^\]]+?)\]/) do |match|
112 type, name = $1, $2
113 "<span class='sig-param'><span class='sig-type'>#{type}</span> <code class='sig-name'>#{name}</code></span>"
114 end
115
116 node.inner_html = content64 node.inner_html = content
117 end65 end
66 end
118 67
119 # Clean up property formatting68 def handle_source_link(node)
120 css('h3').each do |node|69 content = node.next_element&.inner_html
121 node.inner_html = node.inner_html.gsub(/\[property:([^\]]+?)\s+([^\]]+?)\]/) do |match|70 return unless content
122 type, name = $1, $271 content = content.gsub(/<[^>]+>/, '')
123 "<dt class='sig sig-object js'>" \72 if content =~ /src\/(.*?)\.js/
124 "<span class='sig-name descname'>#{name}</span>" \73 path = "/#{$1}.js"
125 "<span class='sig-colon'>:</span> " \74 formatted_link = %Q(<a class="reference external" href="https://github.com/mrdoob/three.js/blob/master/src#{path}">src#{path}</a>)
126 "<span class='sig-type'>#{type}</span></dt>"75 node.next_element.inner_html = formatted_link if node.next_element
76 end
77 end
78
79 def handle_method_signatures(content)
80 content
81 .gsub(PATTERNS[:method_this]) { format_method_signature('this', $1, $2) }
82 .gsub(PATTERNS[:method_return]) do |match|
83 next if $2.start_with?('this')
84 format_method_signature($1, $2, $3, true)
127 end85 end
86 .gsub(PATTERNS[:method_no_params]) { format_method_signature($1, $2, nil, true) }
87 end
88
89 def format_method_signature(type_or_this, name, params_str, with_return = false)
90 params = if params_str
91 params_str.split(',').map { |param| format_parameter(param.strip) }.join("<span class='sig-paren'>, </span>")
128 end92 end
129 93
130 # Clean up external links94 html = "<dt class='sig sig-object js' id='#{name}'>"
95 if type_or_this == 'this'
96 html << "<span class='property'><span class='pre'>this</span></span>."
97 end
98 html << "<span class='sig-name descname'>#{name}</span>" \
99 "<span class='sig-paren'>(</span>" \
100 "#{params}" \
101 "<span class='sig-paren'>)</span>"
102 if with_return
103 html << "<span class='sig-returns'><span class='sig-colon'>:</span> " \
104 "<span class='sig-type'>#{type_or_this}</span></span>"
105 end
106 html << "</dt>"
107 end
108
109 def format_parameter(param)
110 if param.include?(' ')
111 type, name = param.split(' ', 2).map(&:strip)
112 "<span class='sig-param'><span class='sig-type'>#{type}</span> <span class='sig-name'>#{name}</span></span>"
113 else
114 "<span class='sig-param'>#{param}</span>"
115 end
116 end
117
118 def handle_properties(content)
119 content.gsub(PATTERNS[:property]) do |match|
120 type, name = $1, $2
121 "<dt class='sig sig-object js'>" \
122 "<span class='sig-name descname'>#{name}</span>" \
123 "<span class='sig-colon'>:</span> " \
124 "<span class='sig-type'>#{type}</span></dt>"
125 end
126 end
127
128 def handle_name_placeholders(content)
129 content.gsub(PATTERNS[:name_placeholder]) do
130 name = slug.split('/').last.gsub('.html', '')
131 "<span class='descname'>#{name}</span>"
132 end
133 end
134
135 def format_constructor_params(content)
136 content.gsub(PATTERNS[:constructor_param]) do |match|
137 type, name = $1, $2
138 "<span class='sig-param'><span class='sig-type'>#{type}</span> <code class='sig-name'>#{name}</code></span>"
139 end
140 end
141
142 def format_links
131 css('*').each do |node|143 css('*').each do |node|
132 next if node.text?144 next if node.text?
133 145
134 # Handle example links [example:tag Title]146 content = node.inner_html
135 node.inner_html = node.inner_html.gsub(/\[example:([^\s\]]+)\s+([^\]]+)\]/) do |match|147 .gsub(PATTERNS[:example_link]) { create_external_link("https://threejs.org/examples/##{$1}", $2) }
136 tag, title = $1, $2148 .gsub(PATTERNS[:external_link_text]) { create_external_link($1, $2) }
137 "<a class='reference external' href='https://threejs.org/examples/##{tag}'>#{title}</a>"149 .gsub(PATTERNS[:external_link]) { create_external_link($1, $1) }
138 end150 .gsub(PATTERNS[:page_link_text]) { create_internal_link($1, $2) }
139 151 .gsub(PATTERNS[:page_link]) { create_internal_link($1, $1) }
140 # Handle external links with [link:url text] format152
141 node.inner_html = node.inner_html.gsub(/\[link:([^\s\]]+)\s+([^\]]+)\]/) do |match|153 node.inner_html = content
142 url, text = $1, $2154 end
143 "<a class='reference external' href='#{url}'>#{text}</a>"
144 end
145 155
146 # Handle external links with [link:url] format156 normalize_href_attributes
147 node.inner_html = node.inner_html.gsub(/\[link:([^\]]+)\]/) do |match|157 end
148 url = $1
149 "<a class='reference external' href='#{url}'>#{url}</a>"
150 end
151 158
152 # Handle internal page links with text159 def create_external_link(url, text)
153 node.inner_html = node.inner_html.gsub(/\[page:([^\]]+?)\s+([^\]]+?)\]/) do160 %Q(<a class='reference external' href='#{url}'>#{text}</a>)
154 path, text = $1, $2161 end
155 "<a class='reference internal' href='#{path.downcase}'><code class='xref js js-#{path.downcase}'>#{text}</code></a>"
156 end
157 162
158 # Handle internal page links without text163 def create_internal_link(path, text)
159 node.inner_html = node.inner_html.gsub(/\[page:([^\]]+?)\]/) do |match|164 %Q(<a class='reference internal' href='#{path.downcase}'><code class='xref js js-#{path.downcase}'>#{text}</code></a>)
160 path = $1165 end
161 "<a class='reference internal' href='#{path.downcase}'><code class='xref js js-#{path.downcase}'>#{path}</code></a>"
162 end
163 end
164 166
165 # Fix all href attributes to be lowercase and remove .html167 def normalize_href_attributes
166 css('a[href]').each do |link|168 css('a[href]').each do |link|
167 next if link['href'].start_with?('http')169 next if link['href'].start_with?('http')
168 link['href'] = link['href'].remove('../').downcase.sub(/\.html$/, '')170 link['href'] = link['href'].remove('../').downcase.sub(/\.html$/, '')
169 link['class'] = 'reference internal'171 link['class'] = 'reference internal'
170 end172 end
173 end
171 174
172 # Add section classes175 def add_section_structure
173 css('h2').each do |node|176 css('h2').each do |node|
174 node['class'] = 'section-title'177 node['class'] = 'section-title'
175 section = node.next_element178 section = node.next_element
176 if section179 next unless section
177 wrapper = doc.document.create_element('div')180
178 wrapper['class'] = 'section'181 wrapper = doc.document.create_element('div')
179 node.after(wrapper)182 wrapper['class'] = 'section'
180 wrapper.add_child(node)183 node.after(wrapper)
181 current = section184 wrapper.add_child(node)
182 while current && current.name != 'h2'185
183 next_el = current.next186 current = section
184 wrapper.add_child(current)187 while current && current.name != 'h2'
185 current = next_el188 next_el = current.next
186 end189 wrapper.add_child(current)
190 current = next_el
187 end191 end
188 end192 end
189 193
190 # Format description paragraphs194 css('p.desc').each { |node| node['class'] = 'section-desc' }
191 css('p.desc').each do |node|195 end
192 node['class'] = 'section-desc'
193 end
194 196
195 # Handle inline code/backticks in text197 def format_notes
196 css('p, li, dt, dd').each do |node|198 css('p').each do |node|
197 next if node.at_css('pre') # Skip if contains a code block199 next unless node.content.start_with?('Note:')
198 200
199 # Replace backticks with proper code formatting201 wrapper = doc.document.create_element('div')
200 node.inner_html = node.inner_html.gsub(/`([^`]+)`/) do |match|202 wrapper['class'] = 'admonition note'
201 code = $1203
202 "<code class='docutils literal notranslate'><span class='pre'>#{code}</span></code>"204 title = doc.document.create_element('p')
203 end205 title['class'] = 'first admonition-title'
206 title.content = 'Note'
207
208 content = doc.document.create_element('p')
209 content['class'] = 'last'
210 content.inner_html = node.inner_html.sub('Note:', '').strip
211
212 wrapper.add_child(title)
213 wrapper.add_child(content)
214 node.replace(wrapper)
204 end215 end
216 end
205 217
206 # Handle inline code in property descriptions218 def add_heading_attributes
207 css('.property-type').each do |node|
208 node.inner_html = node.inner_html.gsub(/`([^`]+)`/) do |match|
209 code = $1
210 "<code class='docutils literal notranslate'><span class='pre'>#{code}</span></code>"
211 end
212 end
213
214 # Add proper heading IDs and classes
215 css('h1, h2, h3, h4').each do |node|219 css('h1, h2, h3, h4').each do |node|
216 node['id'] ||= node.content.strip.downcase.gsub(/[^\w]+/, '-')220 node['id'] ||= node.content.strip.downcase.gsub(/[^\w]+/, '-')
217 existing_class = node['class'].to_s221 existing_class = node['class'].to_s
218 node['class'] = "#{existing_class} section-header"222 node['class'] = "#{existing_class} section-header"
219 end223 end
220 224
221 # Add note styling225 format_inline_code
222 css('p').each do |node|226 end
223 if node.content.start_with?('Note:')227
224 wrapper = doc.document.create_element('div')228 def format_inline_code
225 wrapper['class'] = 'admonition note'229 selectors = ['p', 'li', 'dt', 'dd', '.property-type'].join(', ')
226 230 css(selectors).each do |node|
227 title = doc.document.create_element('p')231 next if node.at_css('pre')
228 title['class'] = 'first admonition-title'232 node.inner_html = node.inner_html.gsub(PATTERNS[:inline_code]) do |match|
229 title.content = 'Note'233 "<code class='docutils literal notranslate'><span class='pre'>#{$1}</span></code>"
230
231 content = doc.document.create_element('p')
232 content['class'] = 'last'
233 content.inner_html = node.inner_html.sub('Note:', '').strip
234
235 wrapper.add_child(title)
236 wrapper.add_child(content)
237 node.replace(wrapper)
238 end234 end
239 end235 end
240 doc
241 end236 end
242 end237 end
243 end238 end