Module: Html::Merge
- Defined in:
- lib/html/merge.rb,
lib/html/merge/version.rb,
lib/html/merge/provider.rb,
lib/html/merge/node_wrapper.rb,
lib/html/merge/file_analysis.rb,
lib/html/merge/crispr_adapter.rb,
sig/html/merge.rbs
Defined Under Namespace
Modules: Version Classes: CrisprAdapter, Error, FileAnalysis, NodeWrapper, ParseError, Provider
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
- .merge_provider ⇒ Provider
- .parse_html(source, dialect = "html", backend: nil) ⇒ Hash[Symbol, untyped]
- .register_backend! ⇒ nil
- .register_provider!(replace: false) ⇒ Object
- .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]
46 47 48 |
# File 'lib/html/merge.rb', line 46 def available_html_backends html_backend_available_for_analysis?(BACKEND_REFERENCE.id) ? [BACKEND_REFERENCE] : [] end |
.document_context(content:, source_label: "source") ⇒ Object
210 211 212 213 214 215 216 217 218 |
# File 'lib/html/merge.rb', line 210 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
220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/html/merge.rb', line 220 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
243 244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/html/merge.rb', line 243 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
186 187 188 189 190 191 192 |
# File 'lib/html/merge.rb', line 186 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]
50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/html/merge.rb', line 50 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
234 235 236 237 238 239 240 241 |
# File 'lib/html/merge.rb', line 234 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]
38 39 40 41 42 43 44 |
# File 'lib/html/merge.rb', line 38 def html_feature_profile { family: "html", supported_dialects: ["html"], supported_policies: [] } end |
.html_node_span(node) ⇒ Object
155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/html/merge.rb', line 155 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
125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/html/merge.rb', line 125 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
139 140 141 142 143 144 145 |
# File 'lib/html/merge.rb', line 139 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]
62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/html/merge.rb', line 62 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
178 179 180 181 182 183 184 |
# File 'lib/html/merge.rb', line 178 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
168 169 170 171 172 173 174 175 176 |
# File 'lib/html/merge.rb', line 168 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
119 120 121 122 123 |
# File 'lib/html/merge.rb', line 119 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 |
.merge_provider ⇒ Provider
256 257 258 |
# File 'lib/html/merge.rb', line 256 def merge_provider @merge_provider ||= Provider.new end |
.parse_html(source, dialect = "html", backend: nil) ⇒ Hash[Symbol, untyped]
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 115 116 117 |
# File 'lib/html/merge.rb', line 76 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
25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/html/merge.rb', line 25 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 |
.register_provider!(replace: false) ⇒ Object
260 261 262 263 264 |
# File 'lib/html/merge.rb', line 260 def register_provider!(replace: false) return unless Ast::Merge.respond_to?(:register_provider) Ast::Merge.register_provider(merge_provider, replace: replace) end |
.requested_html_backend_id(backend) ⇒ Object
194 195 196 |
# File 'lib/html/merge.rb', line 194 def requested_html_backend_id(backend) backend.to_s.empty? ? BACKEND_REFERENCE.id : backend.to_s end |
.unsupported_feature_result(message) ⇒ Object
198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/html/merge.rb', line 198 def unsupported_feature_result() { ok: false, diagnostics: [{ severity: "error", category: "unsupported_feature", message: }], policies: [] } end |
.visit_html_node(node) {|node| ... } ⇒ Object
147 148 149 150 151 152 153 |
# File 'lib/html/merge.rb', line 147 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 |