search_items.ex (1848B)
1 defmodule ExDoc.Formatter.HTML.SearchItems do 2 @moduledoc false 3 4 # TODO: It should not depend on the parent module 5 # TODO: Add tests that assert on the returned structured, not on JSON 6 alias ExDoc.Formatter.HTML 7 8 def create(nodes, extras) do 9 items = Enum.flat_map(nodes, &module/1) ++ Enum.flat_map(extras, &extra/1) 10 ["searchNodes=" | ExDoc.Utils.to_json(items)] 11 end 12 13 @h2_split_regex ~r/<h2\b.*?>/ 14 @header_body_regex ~r/(?<header>.+)<\/h2>(?<body>.*)/s 15 defp extra(%{id: id, title: title, content: content}) do 16 [intro | sections] = Regex.split(@h2_split_regex, content) 17 intro_json_item = encode("#{id}.html", title, :extras, intro) 18 19 section_json_items = 20 sections 21 |> Enum.map(&Regex.named_captures(@header_body_regex, &1)) 22 |> Enum.map(&extra_section(title, &1["header"], &1["body"], id)) 23 24 [intro_json_item | section_json_items] 25 end 26 27 defp extra_section(title, header, body, id) do 28 header = HTML.strip_tags(header) 29 30 encode( 31 "#{id}.html##{HTML.text_to_id(header)}", 32 "#{title} - #{header}", 33 :extras, 34 body 35 ) 36 end 37 38 defp module(node = %ExDoc.ModuleNode{id: id, type: type, rendered_doc: doc}) do 39 module = encode("#{id}.html", id, type, doc) 40 functions = Enum.map(node.docs, &node_child(&1, id)) 41 types = Enum.map(node.typespecs, &node_child(&1, id)) 42 [module] ++ functions ++ types 43 end 44 45 defp node_child(node, module) do 46 encode( 47 "#{module}.html##{node.id}", 48 "#{module}.#{node.name}/#{node.arity}", 49 node.type, 50 node.rendered_doc 51 ) 52 end 53 54 defp encode(ref, title, type, doc) do 55 %{ref: URI.encode(ref), title: title, type: type, doc: clean_doc(doc)} 56 end 57 58 defp clean_doc(doc) do 59 doc 60 |> Kernel.||("") 61 |> HTML.strip_tags(" ") 62 |> String.replace(~r/\s+/, " ") 63 |> String.trim() 64 end 65 end