Module: Html::Merge
- Defined in:
- lib/html/merge.rb,
lib/html/merge/version.rb,
lib/html/merge/crispr_adapter.rb,
sig/html/merge.rbs
Defined Under Namespace
Modules: Version Classes: CrisprAdapter, Error, ParseError
Constant Summary collapse
- PACKAGE_NAME =
"html-merge"- BACKEND_REFERENCE =
TreeHaver::KREUZBERG_LANGUAGE_PACK_BACKEND
- BACKEND_REGISTRY =
Struct.new(:registered, :mutex).new(false, Mutex.new)
- VERSION =
Current gem version exposed at the traditional constant location.
Version::VERSION
Class Method Summary collapse
- .available_html_backends ⇒ Array[untyped]
- .document_context(content:, source_label: "source") ⇒ Object
- .element_text_selector(text:, id: nil) ⇒ Object
- .ensure_yard_content_wrapper(source) ⇒ String
- .html_backend_available_for_analysis?(backend_id) ⇒ Boolean
- .html_backend_feature_profile(backend: nil) ⇒ Hash[Symbol, untyped]
- .html_descendant_element_text?(node, text) ⇒ Boolean
- .html_feature_profile ⇒ Hash[Symbol, untyped]
- .html_node_span(node) ⇒ Object
- .html_node_summaries(root) ⇒ Object
- .html_parse_error_nodes(root) ⇒ Object
- .html_plan_context(backend: nil) ⇒ Hash[Symbol, untyped]
- .html_point(point) ⇒ Object
- .html_raw_point(node, boundary) ⇒ Object
- .html_root_node(source, backend: nil) ⇒ Object
- .parse_html(source, dialect = "html", backend: nil) ⇒ Hash[Symbol, untyped]
- .register_backend! ⇒ nil
- .requested_html_backend_id(backend) ⇒ Object
- .unsupported_feature_result(message) ⇒ Object
- .visit_html_node(node) {|node| ... } ⇒ Object
Class Method Details
.available_html_backends ⇒ Array[untyped]
43 44 45 |
# File 'lib/html/merge.rb', line 43 def available_html_backends html_backend_available_for_analysis?(BACKEND_REFERENCE.id) ? [BACKEND_REFERENCE] : [] end |
.document_context(content:, source_label: "source") ⇒ Object
207 208 209 210 211 212 213 214 215 |
# File 'lib/html/merge.rb', line 207 def document_context(content:, source_label: "source") require "ast/crispr" Ast::Crispr::DocumentContext.new( content: content, source_label: source_label, adapter: CrisprAdapter.new ) end |
.element_text_selector(text:, id: nil) ⇒ Object
217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/html/merge.rb', line 217 def element_text_selector(text:, id: nil) require "ast/crispr" Ast::Crispr::Selectors.owner_filter( id: id || "html_element_text:#{text}", adapter: CrisprAdapter.new, owner_scope: :html_element ) do |_context, owner| owner.type == "element" && owner.text.include?(text.to_s) && !html_descendant_element_text?(owner.node, text.to_s) end end |
.ensure_yard_content_wrapper(source) ⇒ String
240 241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/html/merge.rb', line 240 def ensure_yard_content_wrapper(source) require "ast/crispr" replacement = %(<div id="content"><h1 class="noborder title">Documentation by YARD</h1></div>\n) target = element_text_selector(text: "Documentation by YARD", id: "yard_title_element") actor = Ast::Crispr::Replace.result(content: source.to_s, target: target, replacement: replacement) if actor.failure? raise Error, actor.respond_to?(:error) ? actor.error.to_s : "HTML CRISPR replacement failed" end actor.updated_content end |
.html_backend_available_for_analysis?(backend_id) ⇒ Boolean
183 184 185 186 187 188 189 |
# File 'lib/html/merge.rb', line 183 def html_backend_available_for_analysis?(backend_id) requested = requested_html_backend_id(backend_id) register_backend! TreeHaver.with_backend(requested) { TreeHaver.parser_for(:html).respond_to?(:parse) } rescue TreeHaver::Error, StandardError false end |
.html_backend_feature_profile(backend: nil) ⇒ Hash[Symbol, untyped]
47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/html/merge.rb', line 47 def html_backend_feature_profile(backend: nil) requested = requested_html_backend_id(backend) unless available_html_backends.any? { |backend_ref| backend_ref.id == requested } return unsupported_feature_result("Unsupported HTML backend #{requested}.") end html_feature_profile.merge( backend: requested, backend_ref: BACKEND_REFERENCE.to_h ) end |
.html_descendant_element_text?(node, text) ⇒ Boolean
231 232 233 234 235 236 237 238 |
# File 'lib/html/merge.rb', line 231 def html_descendant_element_text?(node, text) node.child_count.times.any? do |index| child = node.child(index) next false unless child (child.type == "element" && child.text.include?(text)) || html_descendant_element_text?(child, text) end end |
.html_feature_profile ⇒ Hash[Symbol, untyped]
35 36 37 38 39 40 41 |
# File 'lib/html/merge.rb', line 35 def html_feature_profile { family: "html", supported_dialects: ["html"], supported_policies: [] } end |
.html_node_span(node) ⇒ Object
152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/html/merge.rb', line 152 def html_node_span(node) start_point = html_point(html_raw_point(node, :start)) end_point = html_point(html_raw_point(node, :end)) { start_byte: node.start_byte, end_byte: node.end_byte, start_line: start_point.fetch(:row) + 1, end_line: end_point.fetch(:row) + 1, start_column: start_point.fetch(:column), end_column: end_point.fetch(:column) } end |
.html_node_summaries(root) ⇒ Object
122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/html/merge.rb', line 122 def html_node_summaries(root) nodes = [] visit_html_node(root) do |node| next unless node.structural? nodes << { type: node.type, span: html_node_span(node), text: node.text.to_s.strip[0, 120] } end nodes end |
.html_parse_error_nodes(root) ⇒ Object
136 137 138 139 140 141 142 |
# File 'lib/html/merge.rb', line 136 def html_parse_error_nodes(root) errors = [] visit_html_node(root) do |node| errors << node if node.has_error? || node.type == "ERROR" end errors end |
.html_plan_context(backend: nil) ⇒ Hash[Symbol, untyped]
59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/html/merge.rb', line 59 def html_plan_context(backend: nil) profile = html_backend_feature_profile(backend: backend) return profile if profile[:ok] == false { family_profile: html_feature_profile, feature_profile: { backend: profile[:backend], supports_dialects: false, supported_policies: profile[:supported_policies] } } end |
.html_point(point) ⇒ Object
175 176 177 178 179 180 181 |
# File 'lib/html/merge.rb', line 175 def html_point(point) return { row: point.row, column: point.column } if point.respond_to?(:row) && point.respond_to?(:column) return { row: point[:row], column: point[:column] } if point.respond_to?(:[]) raise Error, "Unsupported TreeHaver point #{point.inspect}" end |
.html_raw_point(node, boundary) ⇒ Object
165 166 167 168 169 170 171 172 173 |
# File 'lib/html/merge.rb', line 165 def html_raw_point(node, boundary) raw_node = node.respond_to?(:inner_node) ? node.inner_node : node methods = boundary == :start ? %i[start_point start_position] : %i[end_point end_position] methods.each do |method_name| return raw_node.public_send(method_name) if raw_node.respond_to?(method_name) end node.public_send(methods.first) end |
.html_root_node(source, backend: nil) ⇒ Object
116 117 118 119 120 |
# File 'lib/html/merge.rb', line 116 def html_root_node(source, backend: nil) requested = requested_html_backend_id(backend) register_backend! TreeHaver.with_backend(requested) { TreeHaver.parser_for(:html).parse(source.to_s).root_node } end |
.parse_html(source, dialect = "html", backend: nil) ⇒ Hash[Symbol, untyped]
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/html/merge.rb', line 73 def parse_html(source, dialect = "html", backend: nil) return unsupported_feature_result("Unsupported HTML dialect #{dialect}.") unless dialect.to_s == "html" requested = requested_html_backend_id(backend) unless available_html_backends.any? { |backend_ref| backend_ref.id == requested } return unsupported_feature_result("Unsupported HTML backend #{requested}.") end register_backend! tree = TreeHaver.with_backend(requested) { TreeHaver.parser_for(:html).parse(source.to_s) } root = tree.root_node diagnostics = html_parse_error_nodes(root).map do |node| { severity: "error", category: "parse_error", message: "HTML parse error in #{node.type}", span: html_node_span(node) } end return { ok: false, diagnostics: diagnostics, policies: [] } unless diagnostics.empty? { ok: true, diagnostics: [], analysis: { dialect: "html", root_kind: root.type, nodes: html_node_summaries(root) }, policies: [] } rescue TreeHaver::Error, StandardError => e { ok: false, diagnostics: [{ severity: "error", category: "parse_error", message: e. }], policies: [] } end |
.register_backend! ⇒ nil
22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/html/merge.rb', line 22 def register_backend! BACKEND_REGISTRY.mutex.synchronize do return if BACKEND_REGISTRY.registered TreeHaver::BackendRegistry.register(BACKEND_REFERENCE) grammar_finder = TreeHaver::GrammarFinder.new(:html) grammar_finder.register! if grammar_finder.available? BACKEND_REGISTRY.registered = true end end |
.requested_html_backend_id(backend) ⇒ Object
191 192 193 |
# File 'lib/html/merge.rb', line 191 def requested_html_backend_id(backend) backend.to_s.empty? ? BACKEND_REFERENCE.id : backend.to_s end |
.unsupported_feature_result(message) ⇒ Object
195 196 197 198 199 200 201 202 203 204 205 |
# File 'lib/html/merge.rb', line 195 def unsupported_feature_result() { ok: false, diagnostics: [{ severity: "error", category: "unsupported_feature", message: }], policies: [] } end |
.visit_html_node(node) {|node| ... } ⇒ Object
144 145 146 147 148 149 150 |
# File 'lib/html/merge.rb', line 144 def visit_html_node(node, &block) yield node node.child_count.times do |index| child = node.child(index) visit_html_node(child, &block) if child end end |